Newer
Older
Fabrice Bellard
committed
/* test if stream is OK (test needed because several SETUP needs
to be done for a given file) */
if (rtp_c->stream != stream) {
rtsp_reply_error(c, RTSP_STATUS_SERVICE);
return;
}
Fabrice Bellard
committed
/* test if stream is already set up */
if (rtp_c->rtp_ctx[stream_index]) {
rtsp_reply_error(c, RTSP_STATUS_STATE);
return;
}
/* check transport */
th = find_transport(h, rtp_c->rtp_protocol);
Ronald S. Bultje
committed
if (!th || (th->lower_transport == RTSP_LOWER_TRANSPORT_UDP &&
Fabrice Bellard
committed
th->client_port_min <= 0)) {
rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
return;
}
/* setup default options */
setup.transport_option[0] = '\0';
dest_addr = rtp_c->from_addr;
dest_addr.sin_port = htons(th->client_port_min);
Fabrice Bellard
committed
/* setup stream */
if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) {
Fabrice Bellard
committed
rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
return;
}
/* now everything is OK, so we can send the connection parameters */
rtsp_reply_header(c, RTSP_STATUS_OK);
/* session ID */
avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
Fabrice Bellard
committed
switch(rtp_c->rtp_protocol) {
Ronald S. Bultje
committed
case RTSP_LOWER_TRANSPORT_UDP:
rtp_port = ff_rtp_get_local_rtp_port(rtp_c->rtp_handles[stream_index]);
rtcp_port = ff_rtp_get_local_rtcp_port(rtp_c->rtp_handles[stream_index]);
avio_printf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
Fabrice Bellard
committed
"client_port=%d-%d;server_port=%d-%d",
th->client_port_min, th->client_port_max,
rtp_port, rtcp_port);
Fabrice Bellard
committed
break;
Ronald S. Bultje
committed
case RTSP_LOWER_TRANSPORT_TCP:
avio_printf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
Fabrice Bellard
committed
stream_index * 2, stream_index * 2 + 1);
break;
default:
break;
}
if (setup.transport_option[0] != '\0')
avio_printf(c->pb, ";%s", setup.transport_option);
avio_printf(c->pb, "\r\n");
Fabrice Bellard
committed
Fabrice Bellard
committed
}
/* find an RTP connection by using the session ID. Check consistency
Fabrice Bellard
committed
with filename */
static HTTPContext *find_rtp_session_with_url(const char *url,
Fabrice Bellard
committed
const char *session_id)
{
HTTPContext *rtp_c;
char path1[1024];
const char *path;
int s, len;
Fabrice Bellard
committed
rtp_c = find_rtp_session(session_id);
if (!rtp_c)
return NULL;
av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
Fabrice Bellard
committed
path = path1;
if (*path == '/')
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;
}
}
len = strlen(path);
if (len > 0 && path[len - 1] == '/' &&
!strncmp(path, rtp_c->stream->filename, len - 1))
return rtp_c;
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 */
avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
avio_printf(c->pb, "\r\n");
Fabrice Bellard
committed
}
static void rtsp_cmd_interrupt(HTTPContext *c, const char *url, RTSPMessageHeader *h, int pause_only)
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;
}
if (pause_only) {
if (rtp_c->state != HTTPSTATE_SEND_DATA &&
rtp_c->state != HTTPSTATE_WAIT_FEED) {
rtsp_reply_error(c, RTSP_STATUS_STATE);
return;
}
rtp_c->state = HTTPSTATE_READY;
rtp_c->first_pts = AV_NOPTS_VALUE;
Fabrice Bellard
committed
}
Fabrice Bellard
committed
/* now everything is OK, so we can send the connection parameters */
rtsp_reply_header(c, RTSP_STATUS_OK);
/* session ID */
Mike Williams
committed
avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
Mike Williams
committed
if (!pause_only)
close_connection(rtp_c);
Fabrice Bellard
committed
}
/********************************************************************/
/* RTP handling */
static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
FFServerStream *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 >= config.nb_max_connections)
Fabrice Bellard
committed
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_freep(&c->buffer);
Fabrice Bellard
committed
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;
ctx->nb_streams = 1;
ctx->streams = av_mallocz_array(ctx->nb_streams, sizeof(AVStream *));
Mike Williams
committed
goto fail;
Fabrice Bellard
committed
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 (ffurl_open(&h, ctx->filename, AVIO_FLAG_WRITE, NULL, NULL) < 0)
Fabrice Bellard
committed
goto fail;
c->rtp_handles[stream_index] = h;
max_packet_size = h->max_packet_size;
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);
/* normally, no packets should be output here, but the packet size may
* be checked */
if (ffio_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) {
Fabrice Bellard
committed
/* XXX: close stream */
goto fail;
}
if (avformat_write_header(ctx, NULL) < 0) {
Fabrice Bellard
committed
fail:
if (h)
av_free(st);
Fabrice Bellard
committed
av_free(ctx);
return -1;
}
avio_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(FFServerStream *stream, AVCodecContext *codec, int copy)
Fabrice Bellard
committed
{
AVStream *fst;
if(stream->nb_streams >= FF_ARRAY_ELEMS(stream->streams))
return NULL;
Fabrice Bellard
committed
fst = av_mallocz(sizeof(AVStream));
if (!fst)
return NULL;
fst->codec = avcodec_alloc_context3(codec->codec);
avcodec_copy_context(fst->codec, codec);
} else {
/* live streams must use the actual feed's codec since it may be
* updated later to carry extradata needed by them.
Fabrice Bellard
committed
fst->priv_data = av_mallocz(sizeof(FeedData));
Philip Gladstone
committed
fst->index = stream->nb_streams;
avpriv_set_pts_info(fst, 33, 1, 90000);
fst->sample_aspect_ratio = codec->sample_aspect_ratio;
Fabrice Bellard
committed
stream->streams[stream->nb_streams++] = fst;
return fst;
}
static int add_av_stream(FFServerStream *feed, AVStream *st)
{
AVStream *fst;
AVCodecContext *av, *av1;
int i;
Michael Niedermayer
committed
av = st->codec;
av1 = feed->streams[i]->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 AVMEDIA_TYPE_AUDIO:
if (av1->channels == av->channels &&
av1->sample_rate == av->sample_rate)
return i;
case AVMEDIA_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 &&
return i;
Philip Gladstone
committed
default:
if (av_stream_get_recommended_encoder_configuration(st))
av_stream_set_recommended_encoder_configuration(fst,
av_strdup(av_stream_get_recommended_encoder_configuration(st)));
static void remove_stream(FFServerStream *stream)
Fabrice Bellard
committed
{
FFServerStream **ps;
ps = &config.first_stream;
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;
infile->flags |= AVFMT_FLAG_NOFILLIN | AVFMT_FLAG_NOPARSE;
mpeg4_count = 0;
for(i=0;i<infile->nb_streams;i++) {
st = infile->streams[i];
if (st->codec->codec_id == AV_CODEC_ID_MPEG4 &&
Michael Niedermayer
committed
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_frame(infile, &pkt) < 0)
break;
st = infile->streams[pkt.stream_index];
if (st->codec->codec_id == AV_CODEC_ID_MPEG4 &&
Michael Niedermayer
committed
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);
st->codec->extradata = av_mallocz(size + FF_INPUT_BUFFER_PADDING_SIZE);
Michael Niedermayer
committed
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
{
FFServerStream *stream, *stream_next;
Fabrice Bellard
committed
/* gather all streams */
for(stream = config.first_stream; stream; stream = stream_next) {
Fabrice Bellard
committed
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 */
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 */
av_dict_set(&stream->in_opts, "mpeg2ts_compute_pcr", "1", 0);
Fabrice Bellard
committed
}
if (!stream->feed_filename[0]) {
http_log("Unspecified feed file for stream '%s'\n", stream->filename);
goto fail;
}
http_log("Opening feed file '%s' for stream '%s'\n", stream->feed_filename, stream->filename);
if ((ret = avformat_open_input(&infile, stream->feed_filename, stream->ifmt, &stream->in_opts)) < 0) {
http_log("Could not open '%s': %s\n", stream->feed_filename, av_err2str(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 (avformat_find_stream_info(infile, NULL) < 0) {
http_log("Could not find codec parameters from '%s'\n",
Fabrice Bellard
committed
stream->feed_filename);
avformat_close_input(&infile);
Fabrice Bellard
committed
goto fail;
}
extract_mpeg4_header(infile);
for(i=0;i<infile->nb_streams;i++)
add_av_stream1(stream, infile->streams[i]->codec, 1);
avformat_close_input(&infile);
Fabrice Bellard
committed
}
}
}
}
static void build_feed_streams(void)
FFServerStream *stream, *feed;
for(stream = config.first_stream; stream; stream = stream->next) {
feed = stream->feed;
if (feed) {
if (stream->is_feed) {
for(i=0;i<stream->nb_streams;i++)
} else {
/* 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]);
for(feed = config.first_feed; feed; feed = feed->next_feed) {
if (avio_check(feed->feed_filename, AVIO_FLAG_READ) > 0) {
/* See if it matches */
int matches = 0;
if (avformat_open_input(&s, feed->feed_filename, NULL, NULL) >= 0) {
Miroslav Slugeň
committed
/* set buffer size */
ffio_set_buf_size(s->pb, FFM_PACKET_SIZE);
/* 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_id) || 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 == AVMEDIA_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 == AVMEDIA_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);
avformat_close_input(&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
}
}
if (avio_check(feed->feed_filename, AVIO_FLAG_WRITE) <= 0) {
AVFormatContext *s = avformat_alloc_context();
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);
}
if (avio_open(&s->pb, feed->feed_filename, AVIO_FLAG_WRITE) < 0) {
http_log("Could not open output feed file '%s'\n",
feed->feed_filename);
s->oformat = feed->fmt;
if (avformat_write_header(s, NULL) < 0) {
http_log("Container doesn't support the required parameters\n");
Alex Beregszaszi
committed
exit(1);
}
av_freep(&s->priv_data);
s->streams = NULL;
s->nb_streams = 0;
avformat_free_context(s);
}
/* 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)
{
FFServerStream *stream;
for(stream = config.first_stream; stream; 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 AVMEDIA_TYPE_AUDIO:
case AVMEDIA_TYPE_VIDEO:
Michael Niedermayer
committed
bandwidth += st->codec->bit_rate;
break;
default:
break;
}
}
stream->bandwidth = (bandwidth + 999) / 1000;
}
}
static void handle_child_exit(int sig)
{
pid_t pid;
int status;
while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
FFServerStream *feed;
for (feed = config.first_feed; feed; feed = feed->next) {
if (feed->pid == pid) {
int uptime = time(0) - feed->pid_start;
feed->pid = 0;
fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
/* Turn off any more restarts */
ffserver_free_child_args(&feed->child_argv);
}
}
}
need_to_start_children = 1;
}
static void opt_debug(void)
config.debug = 1;
snprintf(config.logfilename, sizeof(config.logfilename), "-");
void show_help_default(const char *opt, const char *arg)
printf("usage: ffserver [options]\n"
"Hyper fast multi format Audio/Video streaming server\n");
printf("\n");
show_help_options(options, "Main options:", 0, 0, 0);
}
static const OptionDef options[] = {
#include "cmdutils_common_opts.h"
{ "n", OPT_BOOL, {(void *)&no_launch }, "enable no-launch mode" },
{ "d", 0, {(void*)opt_debug}, "enable debug mode" },
{ "f", HAS_ARG | OPT_STRING, {(void*)&config.filename }, "use configfile instead of /etc/ffserver.conf", "configfile" },
struct sigaction sigact = { { 0 } };
config.filename = av_strdup("/etc/ffserver.conf");
parse_loglevel(argc, argv, options);
av_register_all();
avformat_network_init();
show_banner(argc, argv, options);
parse_options(NULL, argc, argv, options, NULL);
Stefano Sabatini
committed
unsetenv("http_proxy"); /* Kill the http_proxy */
Martin Storsjö
committed
av_lfg_init(&random_state, av_get_random_seed());
sigact.sa_handler = handle_child_exit;
sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
sigaction(SIGCHLD, &sigact, 0);
if ((ret = ffserver_parse_ffconfig(config.filename, &config)) < 0) {
fprintf(stderr, "Error reading configuration file '%s': %s\n",
config.filename, av_err2str(ret));
av_freep(&config.filename);
/* open log file if needed */
if (config.logfilename[0] != '\0') {
if (!strcmp(config.logfilename, "-"))
logfile = fopen(config.logfilename, "a");
av_log_set_callback(http_av_log);
}
Fabrice Bellard
committed
build_file_streams();
compute_bandwidth();
Fabrice Bellard
committed
if (http_server() < 0) {
http_log("Could not start server\n");