#include <string.h>
#include <strings.h>
#include <stdlib.h>
-/* avformat.h defines LIBAVFORMAT_BUILD, include it before all the other libav* headers which use it */
#include "libavformat/avformat.h"
#include "libavformat/network.h"
#include "libavformat/os_support.h"
#include "libavutil/avstring.h"
#include "libavutil/lfg.h"
#include "libavutil/random_seed.h"
-#include "libavutil/intreadwrite.h"
+#include "libavutil/parseutils.h"
#include "libavcodec/opt.h"
#include <stdarg.h>
#include <unistd.h>
#endif
#include <errno.h>
#include <sys/time.h>
-#undef time //needed because HAVE_AV_CONFIG_H is defined on top
#include <time.h>
#include <sys/wait.h>
#include <signal.h>
#include "cmdutils.h"
-#undef exit
-
const char program_name[] = "FFserver";
const int program_birth_year = 2000;
"RTSP_SEND_PACKET",
};
+#if !FF_API_MAX_STREAMS
+#define MAX_STREAMS 20
+#endif
+
#define IOBUFFER_INIT_SIZE 8192
/* timeouts are in ms */
uint8_t *buffer_ptr, *buffer_end;
int http_error;
int post;
+ int chunked_encoding;
+ int chunk_size; /* 0 if it needs to be read */
struct HTTPContext *next;
int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
int64_t data_count;
/* RTSP state specific */
uint8_t *pb_buffer; /* XXX: use that in all the code */
- ByteIOContext *pb;
+ AVIOContext *pb;
int seq; /* RTSP sequence number */
/* RTP state specific */
AVInputFormat *ifmt; /* if non NULL, force input format */
AVOutputFormat *fmt;
IPAddressACL *acl;
+ char dynamic_acl[1024];
int nb_streams;
int prebuffer; /* Number of millseconds early to start */
int64_t max_time; /* Number of milliseconds to run */
static const char *my_program_name;
static const char *my_program_dir;
-static const char *config_filename;
+static const char *config_filename = "/etc/ffserver.conf";
+
static int ffserver_debug;
static int ffserver_daemon;
static int no_launch;
static FILE *logfile = NULL;
+/* FIXME: make ffserver work with IPv6 */
+/* resolve host with also IP address parsing */
+static int resolve_host(struct in_addr *sin_addr, const char *hostname)
+{
+
+ if (!ff_inet_aton(hostname, sin_addr)) {
+#if HAVE_GETADDRINFO
+ struct addrinfo *ai, *cur;
+ struct addrinfo hints;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET;
+ if (getaddrinfo(hostname, NULL, &hints, &ai))
+ return -1;
+ /* getaddrinfo returns a linked list of addrinfo structs.
+ * Even if we set ai_family = AF_INET above, make sure
+ * that the returned one actually is of the correct type. */
+ for (cur = ai; cur; cur = cur->ai_next) {
+ if (cur->ai_family == AF_INET) {
+ *sin_addr = ((struct sockaddr_in *)cur->ai_addr)->sin_addr;
+ freeaddrinfo(ai);
+ return 0;
+ }
+ }
+ freeaddrinfo(ai);
+ return -1;
+#else
+ struct hostent *hp;
+ hp = gethostbyname(hostname);
+ if (!hp)
+ return -1;
+ memcpy(sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
+#endif
+ }
+ return 0;
+}
+
static char *ctime1(char *buf2)
{
time_t ti;
}
}
-void __attribute__ ((format (printf, 1, 2))) http_log(const char *fmt, ...)
+#ifdef __GNUC__
+__attribute__ ((format (printf, 1, 2)))
+#endif
+static void http_log(const char *fmt, ...)
{
va_list vargs;
va_start(vargs, fmt);
second to handle timeouts */
do {
ret = poll(poll_table, poll_entry - poll_table, delay);
- if (ret < 0 && ff_neterrno() != FF_NETERROR(EAGAIN) &&
- ff_neterrno() != FF_NETERROR(EINTR))
+ if (ret < 0 && ff_neterrno() != AVERROR(EAGAIN) &&
+ ff_neterrno() != AVERROR(EINTR))
return -1;
} while (ret < 0);
{
char buffer[300];
int len = snprintf(buffer, sizeof(buffer),
- "HTTP/1.0 200 Server too busy\r\n"
+ "HTTP/1.0 503 Server too busy\r\n"
"Content-type: text/html\r\n"
"\r\n"
"<html><head><title>Too busy</title></head><body>\r\n"
ctx = c->rtp_ctx[i];
if (ctx) {
av_write_trailer(ctx);
+ av_metadata_free(&ctx->metadata);
+ av_free(ctx->streams[0]);
av_free(ctx);
}
h = c->rtp_handles[i];
read_loop:
len = recv(c->fd, c->buffer_ptr, 1, 0);
if (len < 0) {
- if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
- ff_neterrno() != FF_NETERROR(EINTR))
+ if (ff_neterrno() != AVERROR(EAGAIN) &&
+ ff_neterrno() != AVERROR(EINTR))
return -1;
} else if (len == 0) {
return -1;
return 0;
len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
if (len < 0) {
- if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
- ff_neterrno() != FF_NETERROR(EINTR)) {
+ if (ff_neterrno() != AVERROR(EAGAIN) &&
+ ff_neterrno() != AVERROR(EINTR)) {
/* error : close connection */
av_freep(&c->pb_buffer);
return -1;
return 0;
len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
if (len < 0) {
- if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
- ff_neterrno() != FF_NETERROR(EINTR)) {
+ if (ff_neterrno() != AVERROR(EAGAIN) &&
+ ff_neterrno() != AVERROR(EINTR)) {
/* error : close connection */
av_freep(&c->pb_buffer);
return -1;
len = send(c->fd, c->packet_buffer_ptr,
c->packet_buffer_end - c->packet_buffer_ptr, 0);
if (len < 0) {
- if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
- ff_neterrno() != FF_NETERROR(EINTR)) {
+ if (ff_neterrno() != AVERROR(EAGAIN) &&
+ ff_neterrno() != AVERROR(EINTR)) {
/* error : close connection */
av_freep(&c->packet_buffer);
return -1;
return action_required;
}
-
-static void do_switch_stream(HTTPContext *c, int i)
-{
- if (c->switch_feed_streams[i] >= 0) {
-#ifdef PHILIP
- c->feed_streams[i] = c->switch_feed_streams[i];
-#endif
-
- /* Now update the stream */
- }
- c->switch_feed_streams[i] = -1;
-}
-
/* XXX: factorize in utils.c ? */
/* XXX: take care with different space meaning */
static void skip_spaces(const char **pp)
*pp = p;
}
-static int validate_acl(FFStream *stream, HTTPContext *c)
+static void parse_acl_row(FFStream *stream, FFStream* feed, IPAddressACL *ext_acl,
+ const char *p, const char *filename, int line_num)
+{
+ char arg[1024];
+ IPAddressACL acl;
+ int errors = 0;
+
+ get_arg(arg, sizeof(arg), &p);
+ if (strcasecmp(arg, "allow") == 0)
+ acl.action = IP_ALLOW;
+ else if (strcasecmp(arg, "deny") == 0)
+ acl.action = IP_DENY;
+ else {
+ fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
+ filename, line_num, arg);
+ errors++;
+ }
+
+ get_arg(arg, sizeof(arg), &p);
+
+ if (resolve_host(&acl.first, arg) != 0) {
+ fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
+ filename, line_num, arg);
+ errors++;
+ } else
+ acl.last = acl.first;
+
+ get_arg(arg, sizeof(arg), &p);
+
+ if (arg[0]) {
+ if (resolve_host(&acl.last, arg) != 0) {
+ fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
+ filename, line_num, arg);
+ errors++;
+ }
+ }
+
+ if (!errors) {
+ IPAddressACL *nacl = av_mallocz(sizeof(*nacl));
+ IPAddressACL **naclp = 0;
+
+ acl.next = 0;
+ *nacl = acl;
+
+ if (stream)
+ naclp = &stream->acl;
+ else if (feed)
+ naclp = &feed->acl;
+ else if (ext_acl)
+ naclp = &ext_acl;
+ else {
+ fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
+ filename, line_num);
+ errors++;
+ }
+
+ if (naclp) {
+ while (*naclp)
+ naclp = &(*naclp)->next;
+
+ *naclp = nacl;
+ }
+ }
+}
+
+
+static IPAddressACL* parse_dynamic_acl(FFStream *stream, HTTPContext *c)
+{
+ FILE* f;
+ char line[1024];
+ char cmd[1024];
+ IPAddressACL *acl = NULL;
+ int line_num = 0;
+ const char *p;
+
+ f = fopen(stream->dynamic_acl, "r");
+ if (!f) {
+ perror(stream->dynamic_acl);
+ return NULL;
+ }
+
+ acl = av_mallocz(sizeof(IPAddressACL));
+
+ /* Build ACL */
+ for(;;) {
+ if (fgets(line, sizeof(line), f) == NULL)
+ break;
+ line_num++;
+ p = line;
+ while (isspace(*p))
+ p++;
+ if (*p == '\0' || *p == '#')
+ continue;
+ get_arg(cmd, sizeof(cmd), &p);
+
+ if (!strcasecmp(cmd, "ACL"))
+ parse_acl_row(NULL, NULL, acl, p, stream->dynamic_acl, line_num);
+ }
+ fclose(f);
+ return acl;
+}
+
+
+static void free_acl_list(IPAddressACL *in_acl)
+{
+ IPAddressACL *pacl,*pacl2;
+
+ pacl = in_acl;
+ while(pacl) {
+ pacl2 = pacl;
+ pacl = pacl->next;
+ av_freep(pacl2);
+ }
+}
+
+static int validate_acl_list(IPAddressACL *in_acl, HTTPContext *c)
{
enum IPAddressAction last_action = IP_DENY;
IPAddressACL *acl;
struct in_addr *src = &c->from_addr.sin_addr;
unsigned long src_addr = src->s_addr;
- for (acl = stream->acl; acl; acl = acl->next) {
+ for (acl = in_acl; acl; acl = acl->next) {
if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr)
return (acl->action == IP_ALLOW) ? 1 : 0;
last_action = acl->action;
return (last_action == IP_DENY) ? 1 : 0;
}
+static int validate_acl(FFStream *stream, HTTPContext *c)
+{
+ int ret = 0;
+ IPAddressACL *acl;
+
+
+ /* if stream->acl is null validate_acl_list will return 1 */
+ ret = validate_acl_list(stream->acl, c);
+
+ if (stream->dynamic_acl[0]) {
+ acl = parse_dynamic_acl(stream, c);
+
+ ret = validate_acl_list(acl, c);
+
+ free_acl_list(acl);
+ }
+
+ return ret;
+}
+
/* compute the real filename of a file by matching it without its
extensions to all the stream filenames */
static void compute_real_filename(char *filename, int max_size)
}
redir_type = REDIR_NONE;
- if (match_ext(filename, "asx")) {
+ if (av_match_ext(filename, "asx")) {
redir_type = REDIR_ASX;
filename[strlen(filename)-1] = 'f';
- } else if (match_ext(filename, "asf") &&
+ } else if (av_match_ext(filename, "asf") &&
(!useragent || strncasecmp(useragent, "NSPlayer", 8) != 0)) {
/* if this isn't WMP or lookalike, return the redirector file */
redir_type = REDIR_ASF;
- } else if (match_ext(filename, "rpm,ram")) {
+ } else if (av_match_ext(filename, "rpm,ram")) {
redir_type = REDIR_RAM;
strcpy(filename + strlen(filename)-2, "m");
- } else if (match_ext(filename, "rtsp")) {
+ } else if (av_match_ext(filename, "rtsp")) {
redir_type = REDIR_RTSP;
compute_real_filename(filename, sizeof(filename) - 1);
- } else if (match_ext(filename, "sdp")) {
+ } else if (av_match_ext(filename, "sdp")) {
redir_type = REDIR_SDP;
compute_real_filename(filename, sizeof(filename) - 1);
}
if (modify_current_stream(c, ratebuf)) {
for (i = 0; i < FF_ARRAY_ELEMS(c->feed_streams); i++) {
if (c->switch_feed_streams[i] >= 0)
- do_switch_stream(c, i);
+ c->switch_feed_streams[i] = -1;
}
}
}
}
if (c->post == 0 && max_bandwidth < current_bandwidth) {
- c->http_error = 200;
+ c->http_error = 503;
q = c->buffer;
q += snprintf(q, c->buffer_size,
- "HTTP/1.0 200 Server too busy\r\n"
+ "HTTP/1.0 503 Server too busy\r\n"
"Content-type: text/html\r\n"
"\r\n"
"<html><head><title>Too busy</title></head><body>\r\n"
return 0;
}
-static void fmt_bytecount(ByteIOContext *pb, int64_t count)
+static void fmt_bytecount(AVIOContext *pb, int64_t count)
{
static const char *suffix = " kMGTP";
const char *s;
char *p;
time_t ti;
int i, len;
- ByteIOContext *pb;
+ AVIOContext *pb;
if (url_open_dyn_buf(&pb) < 0) {
/* XXX: return an error ? */
AVStream *st = stream->streams[i];
AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
switch(st->codec->codec_type) {
- case CODEC_TYPE_AUDIO:
+ case AVMEDIA_TYPE_AUDIO:
audio_bit_rate += st->codec->bit_rate;
if (codec) {
if (*audio_codec_name)
audio_codec_name = codec->name;
}
break;
- case CODEC_TYPE_VIDEO:
+ case AVMEDIA_TYPE_VIDEO:
video_bit_rate += st->codec->bit_rate;
if (codec) {
if (*video_codec_name)
video_codec_name = codec->name;
}
break;
- case CODEC_TYPE_DATA:
+ case AVMEDIA_TYPE_DATA:
video_bit_rate += st->codec->bit_rate;
break;
default:
parameters[0] = 0;
switch(st->codec->codec_type) {
- case CODEC_TYPE_AUDIO:
+ case AVMEDIA_TYPE_AUDIO:
type = "audio";
snprintf(parameters, sizeof(parameters), "%d channel(s), %d Hz", st->codec->channels, st->codec->sample_rate);
break;
- case CODEC_TYPE_VIDEO:
+ case AVMEDIA_TYPE_VIDEO:
type = "video";
snprintf(parameters, sizeof(parameters), "%dx%d, q=%d-%d, fps=%d", st->codec->width, st->codec->height,
st->codec->qmin, st->codec->qmax, st->codec->time_base.den / st->codec->time_base.num);
strcpy(input_filename, c->stream->feed->feed_filename);
buf_size = FFM_PACKET_SIZE;
/* compute position (absolute time) */
- if (find_info_tag(buf, sizeof(buf), "date", info)) {
- stream_pos = parse_date(buf, 0);
- if (stream_pos == INT64_MIN)
- return -1;
- } else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
+ if (av_find_info_tag(buf, sizeof(buf), "date", info)) {
+ if ((ret = av_parse_time(&stream_pos, buf, 0)) < 0)
+ return ret;
+ } else if (av_find_info_tag(buf, sizeof(buf), "buffer", info)) {
int prebuffer = strtol(buf, 0, 10);
stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
} else
strcpy(input_filename, c->stream->feed_filename);
buf_size = 0;
/* compute position (relative time) */
- if (find_info_tag(buf, sizeof(buf), "date", info)) {
- stream_pos = parse_date(buf, 1);
- if (stream_pos == INT64_MIN)
- return -1;
+ if (av_find_info_tag(buf, sizeof(buf), "date", info)) {
+ if ((ret = av_parse_time(&stream_pos, buf, 1)) < 0)
+ return ret;
} else
stream_pos = 0;
}
c->pts_stream_index = 0;
for(i=0;i<c->stream->nb_streams;i++) {
if (c->pts_stream_index == 0 &&
- c->stream->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO) {
+ c->stream->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
c->pts_stream_index = i;
}
}
switch(c->state) {
case HTTPSTATE_SEND_DATA_HEADER:
memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
- av_metadata_set(&c->fmt_ctx.metadata, "author" ,c->stream->author);
- av_metadata_set(&c->fmt_ctx.metadata, "comment" ,c->stream->comment);
- av_metadata_set(&c->fmt_ctx.metadata, "copyright",c->stream->copyright);
- av_metadata_set(&c->fmt_ctx.metadata, "title" ,c->stream->title);
+ av_metadata_set2(&c->fmt_ctx.metadata, "author" , c->stream->author , 0);
+ av_metadata_set2(&c->fmt_ctx.metadata, "comment" , c->stream->comment , 0);
+ av_metadata_set2(&c->fmt_ctx.metadata, "copyright", c->stream->copyright, 0);
+ av_metadata_set2(&c->fmt_ctx.metadata, "title" , c->stream->title , 0);
for(i=0;i<c->stream->nb_streams;i++) {
AVStream *st;
http_log("Error writing output header\n");
return -1;
}
+ av_metadata_free(&c->fmt_ctx.metadata);
len = url_close_dyn_buf(c->fmt_ctx.pb, &c->pb_buffer);
c->buffer_ptr = c->pb_buffer;
else {
AVPacket pkt;
redo:
- if (av_read_frame(c->fmt_in, &pkt) < 0) {
- if (c->stream->feed && c->stream->feed->feed_opened) {
+ ret = av_read_frame(c->fmt_in, &pkt);
+ if (ret < 0) {
+ if (c->stream->feed) {
/* if coming from feed, it means we reached the end of the
ffm file, so must wait for more data */
c->state = HTTPSTATE_WAIT_FEED;
return 1; /* state changed */
+ } else if (ret == AVERROR(EAGAIN)) {
+ /* input not ready, come back later */
+ return 0;
} else {
if (c->stream->loop) {
av_close_input_file(c->fmt_in);
c->switch_pending = 0;
for(i=0;i<c->stream->nb_streams;i++) {
if (c->switch_feed_streams[i] == pkt.stream_index)
- if (pkt.flags & PKT_FLAG_KEY)
- do_switch_stream(c, i);
+ if (pkt.flags & AV_PKT_FLAG_KEY)
+ c->switch_feed_streams[i] = -1;
if (c->switch_feed_streams[i] >= 0)
c->switch_pending = 1;
}
}
for(i=0;i<c->stream->nb_streams;i++) {
- if (c->feed_streams[i] == pkt.stream_index) {
+ if (c->stream->feed_streams[i] == pkt.stream_index) {
AVStream *st = c->fmt_in->streams[source_index];
pkt.stream_index = i;
- if (pkt.flags & PKT_FLAG_KEY &&
- (st->codec->codec_type == CODEC_TYPE_VIDEO ||
+ if (pkt.flags & AV_PKT_FLAG_KEY &&
+ (st->codec->codec_type == AVMEDIA_TYPE_VIDEO ||
c->stream->nb_streams == 1))
c->got_key_frame = 1;
if (!c->stream->send_on_key || c->got_key_frame)
if (c->is_packetized) {
/* compute send time and duration */
c->cur_pts = av_rescale_q(pkt.dts, ist->time_base, AV_TIME_BASE_Q);
- if (ist->start_time != AV_NOPTS_VALUE)
- c->cur_pts -= av_rescale_q(ist->start_time, ist->time_base, AV_TIME_BASE_Q);
+ c->cur_pts -= c->first_pts;
c->cur_frame_duration = av_rescale_q(pkt.duration, ist->time_base, AV_TIME_BASE_Q);
/* find RTP context */
c->packet_stream_index = pkt.stream_index;
if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP) {
/* RTP packets are sent inside the RTSP TCP connection */
- ByteIOContext *pb;
+ AVIOContext *pb;
int interleaved_index, size;
uint8_t header[4];
HTTPContext *rtsp_c;
header[1] = interleaved_index;
header[2] = len >> 8;
header[3] = len;
- put_buffer(pb, header, 4);
+ avio_write(pb, header, 4);
/* write RTP packet data */
c->buffer_ptr += 4;
- put_buffer(pb, c->buffer_ptr, len);
+ avio_write(pb, c->buffer_ptr, len);
size = url_close_dyn_buf(pb, &c->packet_buffer);
/* prepare asynchronous TCP sending */
rtsp_c->packet_buffer_ptr = c->packet_buffer;
/* TCP data output */
len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
if (len < 0) {
- if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
- ff_neterrno() != FF_NETERROR(EINTR))
+ if (ff_neterrno() != AVERROR(EAGAIN) &&
+ ff_neterrno() != AVERROR(EINTR))
/* error : close connection */
return -1;
else
c->buffer_ptr = c->buffer;
c->buffer_end = c->buffer + FFM_PACKET_SIZE;
c->stream->feed_opened = 1;
+ c->chunked_encoding = !!av_stristr(c->buffer, "Transfer-Encoding: chunked");
return 0;
}
static int http_receive_data(HTTPContext *c)
{
HTTPContext *c1;
+ int len, loop_run = 0;
- if (c->buffer_end > c->buffer_ptr) {
- int len;
+ while (c->chunked_encoding && !c->chunk_size &&
+ c->buffer_end > c->buffer_ptr) {
+ /* read chunk header, if present */
+ len = recv(c->fd, c->buffer_ptr, 1, 0);
- len = recv(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
if (len < 0) {
- if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
- ff_neterrno() != FF_NETERROR(EINTR))
+ if (ff_neterrno() != AVERROR(EAGAIN) &&
+ ff_neterrno() != AVERROR(EINTR))
+ /* error : close connection */
+ goto fail;
+ return 0;
+ } else if (len == 0) {
+ /* end of connection : close it */
+ goto fail;
+ } else if (c->buffer_ptr - c->buffer >= 2 &&
+ !memcmp(c->buffer_ptr - 1, "\r\n", 2)) {
+ c->chunk_size = strtol(c->buffer, 0, 16);
+ if (c->chunk_size == 0) // end of stream
+ goto fail;
+ c->buffer_ptr = c->buffer;
+ break;
+ } else if (++loop_run > 10) {
+ /* no chunk header, abort */
+ goto fail;
+ } else {
+ c->buffer_ptr++;
+ }
+ }
+
+ if (c->buffer_end > c->buffer_ptr) {
+ len = recv(c->fd, c->buffer_ptr,
+ FFMIN(c->chunk_size, c->buffer_end - c->buffer_ptr), 0);
+ if (len < 0) {
+ if (ff_neterrno() != AVERROR(EAGAIN) &&
+ ff_neterrno() != AVERROR(EINTR))
/* error : close connection */
goto fail;
} else if (len == 0)
/* end of connection : close it */
goto fail;
else {
+ c->chunk_size -= len;
c->buffer_ptr += len;
c->data_count += len;
update_datarate(&c->datarate, c->data_count);
} else {
/* We have a header in our hands that contains useful data */
AVFormatContext *s = NULL;
- ByteIOContext *pb;
+ AVIOContext *pb;
AVInputFormat *fmt_in;
int i;
for (i = 0; i < s->nb_streams; i++) {
AVStream *fst = feed->streams[i];
AVStream *st = s->streams[i];
- memcpy(fst->codec, st->codec, sizeof(AVCodecContext));
- if (fst->codec->extradata_size) {
- fst->codec->extradata = av_malloc(fst->codec->extradata_size);
- if (!fst->codec->extradata)
- goto fail;
- memcpy(fst->codec->extradata, st->codec->extradata,
- fst->codec->extradata_size);
- }
+ avcodec_copy_context(fst->codec, st->codec);
}
av_close_input_stream(s);
{
const char *str;
time_t ti;
- char *p;
+ struct tm *tm;
char buf2[32];
switch(error_number) {
/* output GMT time */
ti = time(NULL);
- p = ctime(&ti);
- strcpy(buf2, p);
- p = buf2 + strlen(p) - 1;
- if (*p == '\n')
- *p = '\0';
+ tm = gmtime(&ti);
+ strftime(buf2, sizeof(buf2), "%a, %d %b %Y %H:%M:%S", tm);
url_fprintf(c->pb, "Date: %s GMT\r\n", buf2);
}
if (*p == '\n')
p++;
while (*p != '\0') {
- p1 = strchr(p, '\n');
+ p1 = memchr(p, '\n', (char *)c->buffer_ptr - p);
if (!p1)
break;
p2 = p1;
len = sizeof(line) - 1;
memcpy(line, p, len);
line[len] = '\0';
- rtsp_parse_line(header, line);
+ ff_rtsp_parse_line(header, line, NULL, NULL);
p = p1 + 1;
}
struct in_addr my_ip)
{
AVFormatContext *avc;
- AVStream avs[MAX_STREAMS];
+ AVStream *avs = NULL;
int i;
avc = avformat_alloc_context();
if (avc == NULL) {
return -1;
}
- av_metadata_set(&avc->metadata, "title",
- stream->title[0] ? stream->title : "No Title");
+ av_metadata_set2(&avc->metadata, "title",
+ stream->title[0] ? stream->title : "No Title", 0);
avc->nb_streams = stream->nb_streams;
if (stream->is_multicast) {
snprintf(avc->filename, 1024, "rtp://%s:%d?multicast=1?ttl=%d",
inet_ntoa(stream->multicast_ip),
stream->multicast_port, stream->multicast_ttl);
+ } else {
+ snprintf(avc->filename, 1024, "rtp://0.0.0.0");
}
+#if !FF_API_MAX_STREAMS
+ if (avc->nb_streams >= INT_MAX/sizeof(*avc->streams) ||
+ !(avc->streams = av_malloc(avc->nb_streams * sizeof(*avc->streams))))
+ goto sdp_done;
+#endif
+ if (avc->nb_streams >= INT_MAX/sizeof(*avs) ||
+ !(avs = av_malloc(avc->nb_streams * sizeof(*avs))))
+ goto sdp_done;
+
for(i = 0; i < stream->nb_streams; i++) {
avc->streams[i] = &avs[i];
avc->streams[i]->codec = stream->streams[i]->codec;
}
*pbuffer = av_mallocz(2048);
avf_sdp_create(&avc, 1, *pbuffer, 2048);
+
+ sdp_done:
+#if !FF_API_MAX_STREAMS
+ av_free(avc->streams);
+#endif
+ av_metadata_free(&avc->metadata);
av_free(avc);
+ av_free(avs);
return strlen(*pbuffer);
}
struct sockaddr_in my_addr;
/* find which url is asked */
- url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
+ av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
path = path1;
if (*path == '/')
path++;
return;
}
rtsp_reply_header(c, RTSP_STATUS_OK);
+ url_fprintf(c->pb, "Content-Base: %s/\r\n", url);
url_fprintf(c->pb, "Content-Type: application/sdp\r\n");
url_fprintf(c->pb, "Content-Length: %d\r\n", content_length);
url_fprintf(c->pb, "\r\n");
- put_buffer(c->pb, content, content_length);
+ avio_write(c->pb, content, content_length);
+ av_free(content);
}
static HTTPContext *find_rtp_session(const char *session_id)
RTSPMessageHeader *h)
{
FFStream *stream;
- int stream_index, port;
+ int stream_index, rtp_port, rtcp_port;
char buf[1024];
char path1[1024];
const char *path;
RTSPActionServerSetup setup;
/* find which url is asked */
- url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
+ av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
path = path1;
if (*path == '/')
path++;
switch(rtp_c->rtp_protocol) {
case RTSP_LOWER_TRANSPORT_UDP:
- port = rtp_get_local_port(rtp_c->rtp_handles[stream_index]);
+ rtp_port = rtp_get_local_rtp_port(rtp_c->rtp_handles[stream_index]);
+ rtcp_port = rtp_get_local_rtcp_port(rtp_c->rtp_handles[stream_index]);
url_fprintf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
"client_port=%d-%d;server_port=%d-%d",
- th->client_port_min, th->client_port_min + 1,
- port, port + 1);
+ th->client_port_min, th->client_port_max,
+ rtp_port, rtcp_port);
break;
case RTSP_LOWER_TRANSPORT_TCP:
url_fprintf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
char path1[1024];
const char *path;
char buf[1024];
- int s;
+ int s, len;
rtp_c = find_rtp_session(session_id);
if (!rtp_c)
return NULL;
/* find which url is asked */
- url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
+ av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
path = path1;
if (*path == '/')
path++;
return rtp_c;
}
}
+ len = strlen(path);
+ if (len > 0 && path[len - 1] == '/' &&
+ !strncmp(path, rtp_c->stream->filename, len - 1))
+ return rtp_c;
return NULL;
}
ctx = avformat_alloc_context();
if (!ctx)
return -1;
- ctx->oformat = guess_format("rtp", NULL, NULL);
+ ctx->oformat = av_guess_format("rtp", NULL, NULL);
st = av_mallocz(sizeof(AVStream));
if (!st)
goto fail;
- st->codec= avcodec_alloc_context();
ctx->nb_streams = 1;
ctx->streams[0] = st;
/********************************************************************/
/* ffserver initialization */
-static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec)
+static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec, int copy)
{
AVStream *fst;
fst = av_mallocz(sizeof(AVStream));
if (!fst)
return NULL;
- fst->codec= avcodec_alloc_context();
+ if (copy) {
+ fst->codec= avcodec_alloc_context();
+ memcpy(fst->codec, codec, sizeof(AVCodecContext));
+ if (codec->extradata_size) {
+ fst->codec->extradata = av_malloc(codec->extradata_size);
+ memcpy(fst->codec->extradata, codec->extradata,
+ codec->extradata_size);
+ }
+ } else {
+ /* live streams must use the actual feed's codec since it may be
+ * updated later to carry extradata needed by the streams.
+ */
+ fst->codec = codec;
+ }
fst->priv_data = av_mallocz(sizeof(FeedData));
- memcpy(fst->codec, codec, sizeof(AVCodecContext));
fst->index = stream->nb_streams;
av_set_pts_info(fst, 33, 1, 90000);
+ fst->sample_aspect_ratio = codec->sample_aspect_ratio;
stream->streams[stream->nb_streams++] = fst;
return fst;
}
av1->bit_rate == av->bit_rate) {
switch(av->codec_type) {
- case CODEC_TYPE_AUDIO:
+ case AVMEDIA_TYPE_AUDIO:
if (av1->channels == av->channels &&
av1->sample_rate == av->sample_rate)
goto found;
break;
- case CODEC_TYPE_VIDEO:
+ case AVMEDIA_TYPE_VIDEO:
if (av1->width == av->width &&
av1->height == av->height &&
av1->time_base.den == av->time_base.den &&
}
}
- fst = add_av_stream1(feed, av);
+ fst = add_av_stream1(feed, av, 0);
if (!fst)
return -1;
return feed->nb_streams - 1;
extract_mpeg4_header(infile);
for(i=0;i<infile->nb_streams;i++)
- add_av_stream1(stream, infile->streams[i]->codec);
+ add_av_stream1(stream, infile->streams[i]->codec, 1);
av_close_input_file(infile);
}
ccs = ss->codec;
#define CHECK_CODEC(x) (ccf->x != ccs->x)
- if (CHECK_CODEC(codec) || CHECK_CODEC(codec_type)) {
+ 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 == CODEC_TYPE_VIDEO) {
+ } else if (ccf->codec_type == AVMEDIA_TYPE_VIDEO) {
if (CHECK_CODEC(time_base.den) ||
CHECK_CODEC(time_base.num) ||
CHECK_CODEC(width) ||
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) {
+ } else if (ccf->codec_type == AVMEDIA_TYPE_AUDIO) {
if (CHECK_CODEC(sample_rate) ||
CHECK_CODEC(channels) ||
CHECK_CODEC(frame_size)) {
}
/* only write the header of the ffm file */
- if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
+ if (avio_open(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
http_log("Could not open output feed file '%s'\n",
feed->feed_filename);
exit(1);
}
/* XXX: need better api */
av_freep(&s->priv_data);
- url_fclose(s->pb);
+ avio_close(s->pb);
}
/* get feed size and write index */
fd = open(feed->feed_filename, O_RDONLY);
for(i=0;i<stream->nb_streams;i++) {
AVStream *st = stream->streams[i];
switch(st->codec->codec_type) {
- case CODEC_TYPE_AUDIO:
- case CODEC_TYPE_VIDEO:
+ case AVMEDIA_TYPE_AUDIO:
+ case AVMEDIA_TYPE_VIDEO:
bandwidth += st->codec->bit_rate;
break;
default:
/* compute default parameters */
switch(av->codec_type) {
- case CODEC_TYPE_AUDIO:
+ case AVMEDIA_TYPE_AUDIO:
if (av->bit_rate == 0)
av->bit_rate = 64000;
if (av->sample_rate == 0)
if (av->channels == 0)
av->channels = 1;
break;
- case CODEC_TYPE_VIDEO:
+ case AVMEDIA_TYPE_VIDEO:
if (av->bit_rate == 0)
av->bit_rate = 64000;
if (av->time_base.num == 0){
av->nsse_weight = 8;
av->frame_skip_cmp = FF_CMP_DCTMAX;
- av->me_method = ME_EPZS;
+ if (!av->me_method)
+ av->me_method = ME_EPZS;
av->rc_buffer_aggressivity = 1.0;
if (!av->rc_eq)
{
AVCodec *p= avcodec_find_encoder_by_name(arg);
- if (p == NULL || p->type != CODEC_TYPE_AUDIO)
+ if (p == NULL || p->type != AVMEDIA_TYPE_AUDIO)
return CODEC_ID_NONE;
return p->id;
{
AVCodec *p= avcodec_find_encoder_by_name(arg);
- if (p == NULL || p->type != CODEC_TYPE_VIDEO)
+ if (p == NULL || p->type != AVMEDIA_TYPE_VIDEO)
return CODEC_ID_NONE;
return p->id;
return ret;
}
+static int ffserver_opt_preset(const char *arg,
+ AVCodecContext *avctx, int type,
+ enum CodecID *audio_id, enum CodecID *video_id)
+{
+ FILE *f=NULL;
+ char filename[1000], tmp[1000], tmp2[1000], line[1000];
+ int ret = 0;
+ AVCodec *codec = avcodec_find_encoder(avctx->codec_id);
+
+ if (!(f = get_preset_file(filename, sizeof(filename), arg, 0,
+ codec ? codec->name : NULL))) {
+ fprintf(stderr, "File for preset '%s' not found\n", arg);
+ return 1;
+ }
+
+ while(!feof(f)){
+ int e= fscanf(f, "%999[^\n]\n", line) - 1;
+ if(line[0] == '#' && !e)
+ continue;
+ e|= sscanf(line, "%999[^=]=%999[^\n]\n", tmp, tmp2) - 2;
+ if(e){
+ fprintf(stderr, "%s: Invalid syntax: '%s'\n", filename, line);
+ ret = 1;
+ break;
+ }
+ if(!strcmp(tmp, "acodec")){
+ *audio_id = opt_audio_codec(tmp2);
+ }else if(!strcmp(tmp, "vcodec")){
+ *video_id = opt_video_codec(tmp2);
+ }else if(!strcmp(tmp, "scodec")){
+ /* opt_subtitle_codec(tmp2); */
+ }else if(ffserver_opt_default(tmp, tmp2, avctx, type) < 0){
+ fprintf(stderr, "%s: Invalid option or argument: '%s', parsed as '%s' = '%s'\n", filename, line, tmp, tmp2);
+ ret = 1;
+ break;
+ }
+ }
+
+ fclose(f);
+
+ 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 void report_config_error(const char *filename, int line_num, int *errors, const char *fmt, ...)
+{
+ va_list vl;
+ va_start(vl, fmt);
+ fprintf(stderr, "%s:%d: ", filename, line_num);
+ vfprintf(stderr, fmt, vl);
+ va_end(vl);
+
+ (*errors)++;
+}
+
static int parse_ffconfig(const char *filename)
{
FILE *f;
redirect = NULL;
audio_id = CODEC_ID_NONE;
video_id = CODEC_ID_NONE;
+
+#define ERROR(...) report_config_error(filename, line_num, &errors, __VA_ARGS__)
for(;;) {
if (fgets(line, sizeof(line), f) == NULL)
break;
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++;
+ ERROR("Invalid_port: %s\n", arg);
}
my_http_addr.sin_port = htons(val);
} else if (!strcasecmp(cmd, "BindAddress")) {
get_arg(arg, sizeof(arg), &p);
if (resolve_host(&my_http_addr.sin_addr, arg) != 0) {
- fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
- filename, line_num, arg);
- errors++;
+ ERROR("%s:%d: Invalid host/IP address: %s\n", arg);
}
} else if (!strcasecmp(cmd, "NoDaemon")) {
ffserver_daemon = 0;
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++;
+ ERROR("%s:%d: Invalid port: %s\n", arg);
}
my_rtsp_addr.sin_port = htons(atoi(arg));
} else if (!strcasecmp(cmd, "RTSPBindAddress")) {
get_arg(arg, sizeof(arg), &p);
if (resolve_host(&my_rtsp_addr.sin_addr, arg) != 0) {
- fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
- filename, line_num, arg);
- errors++;
+ ERROR("Invalid host/IP address: %s\n", arg);
}
} 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++;
+ ERROR("Invalid MaxHTTPConnections: %s\n", arg);
}
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++;
+ ERROR("Invalid MaxClients: %s\n", arg);
} else {
nb_max_connections = val;
}
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++;
+ ERROR("Invalid MaxBandwidth: %s\n", arg);
} else
max_bandwidth = llval;
} else if (!strcasecmp(cmd, "CustomLog")) {
/* Feed related options */
char *q;
if (stream || feed) {
- fprintf(stderr, "%s:%d: Already in a tag\n",
- filename, line_num);
+ ERROR("Already in a tag\n");
} else {
feed = av_mallocz(sizeof(FFStream));
get_arg(feed->filename, sizeof(feed->filename), &p);
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++;
+ ERROR("Feed '%s' already registered\n", s->filename);
}
}
- feed->fmt = guess_format("ffm", NULL, NULL);
+ 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 = (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++;
+ ERROR("Feed max file size is too small, must be at least %d\n", FFM_PACKET_SIZE*4);
}
}
} else if (!strcasecmp(cmd, "</Feed>")) {
if (!feed) {
- fprintf(stderr, "%s:%d: No corresponding <Feed> for </Feed>\n",
- filename, line_num);
- errors++;
+ ERROR("No corresponding <Feed> for </Feed>\n");
}
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);
+ ERROR("Already in a tag\n");
} else {
FFStream *s;
- const AVClass *class;
stream = av_mallocz(sizeof(FFStream));
get_arg(stream->filename, sizeof(stream->filename), &p);
q = strrchr(stream->filename, '>');
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++;
+ ERROR("Stream '%s' already registered\n", s->filename);
}
}
- 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;
+ stream->fmt = ffserver_guess_format(NULL, stream->filename, NULL);
+ avcodec_get_context_defaults2(&video_enc, AVMEDIA_TYPE_VIDEO);
+ avcodec_get_context_defaults2(&audio_enc, AVMEDIA_TYPE_AUDIO);
audio_id = CODEC_ID_NONE;
video_id = CODEC_ID_NONE;
if (stream->fmt) {
sfeed = sfeed->next_feed;
}
if (!sfeed)
- fprintf(stderr, "%s:%d: feed '%s' not defined\n",
- filename, line_num, arg);
+ ERROR("feed '%s' not defined\n", arg);
else
stream->feed = sfeed;
}
/* 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);
+ stream->fmt = ffserver_guess_format(arg, NULL, NULL);
if (!stream->fmt) {
- fprintf(stderr, "%s:%d: Unknown Format: %s\n",
- filename, line_num, arg);
- errors++;
+ ERROR("Unknown Format: %s\n", arg);
}
}
if (stream->fmt) {
if (stream) {
stream->ifmt = av_find_input_format(arg);
if (!stream->ifmt) {
- fprintf(stderr, "%s:%d: Unknown input format: %s\n",
- filename, line_num, arg);
+ ERROR("Unknown input format: %s\n", arg);
}
}
} else if (!strcasecmp(cmd, "FaviconURL")) {
if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
} else {
- fprintf(stderr, "%s:%d: FaviconURL only permitted for status streams\n",
- filename, line_num);
- errors++;
+ ERROR("FaviconURL only permitted for status streams\n");
}
} else if (!strcasecmp(cmd, "Author")) {
if (stream)
get_arg(arg, sizeof(arg), &p);
audio_id = opt_audio_codec(arg);
if (audio_id == CODEC_ID_NONE) {
- fprintf(stderr, "%s:%d: Unknown AudioCodec: %s\n",
- filename, line_num, arg);
- errors++;
+ ERROR("Unknown AudioCodec: %s\n", arg);
}
} else if (!strcasecmp(cmd, "VideoCodec")) {
get_arg(arg, sizeof(arg), &p);
video_id = opt_video_codec(arg);
if (video_id == CODEC_ID_NONE) {
- fprintf(stderr, "%s:%d: Unknown VideoCodec: %s\n",
- filename, line_num, arg);
- errors++;
+ ERROR("Unknown VideoCodec: %s\n", arg);
}
} else if (!strcasecmp(cmd, "MaxTime")) {
get_arg(arg, sizeof(arg), &p);
} else if (!strcasecmp(cmd, "AudioBitRate")) {
get_arg(arg, sizeof(arg), &p);
if (stream)
- audio_enc.bit_rate = atoi(arg) * 1000;
+ audio_enc.bit_rate = lrintf(atof(arg) * 1000);
} else if (!strcasecmp(cmd, "AudioChannels")) {
get_arg(arg, sizeof(arg), &p);
if (stream)
video_enc.rc_min_rate = minrate * 1000;
video_enc.rc_max_rate = maxrate * 1000;
} else {
- fprintf(stderr, "%s:%d: Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n",
- filename, line_num, arg);
- errors++;
+ ERROR("Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n", arg);
}
}
} else if (!strcasecmp(cmd, "Debug")) {
} else if (!strcasecmp(cmd, "VideoSize")) {
get_arg(arg, sizeof(arg), &p);
if (stream) {
- av_parse_video_frame_size(&video_enc.width, &video_enc.height, arg);
+ av_parse_video_size(&video_enc.width, &video_enc.height, arg);
if ((video_enc.width % 16) != 0 ||
(video_enc.height % 16) != 0) {
- fprintf(stderr, "%s:%d: Image size must be a multiple of 16\n",
- filename, line_num);
- errors++;
+ ERROR("Image size must be a multiple of 16\n");
}
}
} else if (!strcasecmp(cmd, "VideoFrameRate")) {
get_arg(arg, sizeof(arg), &p);
if (stream) {
AVRational frame_rate;
- if (av_parse_video_frame_rate(&frame_rate, arg) < 0) {
- fprintf(stderr, "Incorrect frame rate\n");
- errors++;
+ if (av_parse_video_rate(&frame_rate, arg) < 0) {
+ ERROR("Incorrect frame rate: %s\n", arg);
} else {
video_enc.time_base.num = frame_rate.den;
video_enc.time_base.den = frame_rate.num;
type = AV_OPT_FLAG_AUDIO_PARAM;
}
if (ffserver_opt_default(arg, arg2, avctx, type|AV_OPT_FLAG_ENCODING_PARAM)) {
- fprintf(stderr, "AVOption error: %s %s\n", arg, arg2);
- errors++;
+ ERROR("AVOption error: %s %s\n", arg, arg2);
+ }
+ } else if (!strcasecmp(cmd, "AVPresetVideo") ||
+ !strcasecmp(cmd, "AVPresetAudio")) {
+ AVCodecContext *avctx;
+ int type;
+ get_arg(arg, sizeof(arg), &p);
+ if (!strcasecmp(cmd, "AVPresetVideo")) {
+ avctx = &video_enc;
+ video_enc.codec_id = video_id;
+ type = AV_OPT_FLAG_VIDEO_PARAM;
+ } else {
+ avctx = &audio_enc;
+ audio_enc.codec_id = audio_id;
+ type = AV_OPT_FLAG_AUDIO_PARAM;
+ }
+ if (ffserver_opt_preset(arg, avctx, type|AV_OPT_FLAG_ENCODING_PARAM, &audio_id, &video_id)) {
+ ERROR("AVPreset error: %s\n", arg);
}
} else if (!strcasecmp(cmd, "VideoTag")) {
get_arg(arg, sizeof(arg), &p);
if ((strlen(arg) == 4) && stream)
- video_enc.codec_tag = AV_RL32(arg);
+ video_enc.codec_tag = MKTAG(arg[0], arg[1], arg[2], arg[3]);
} else if (!strcasecmp(cmd, "BitExact")) {
if (stream)
video_enc.flags |= CODEC_FLAG_BITEXACT;
if (stream) {
video_enc.max_qdiff = atoi(arg);
if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
- fprintf(stderr, "%s:%d: VideoQDiff out of range\n",
- filename, line_num);
- errors++;
+ ERROR("VideoQDiff out of range\n");
}
}
} else if (!strcasecmp(cmd, "VideoQMax")) {
if (stream) {
video_enc.qmax = atoi(arg);
if (video_enc.qmax < 1 || video_enc.qmax > 31) {
- fprintf(stderr, "%s:%d: VideoQMax out of range\n",
- filename, line_num);
- errors++;
+ ERROR("VideoQMax out of range\n");
}
}
} else if (!strcasecmp(cmd, "VideoQMin")) {
if (stream) {
video_enc.qmin = atoi(arg);
if (video_enc.qmin < 1 || video_enc.qmin > 31) {
- fprintf(stderr, "%s:%d: VideoQMin out of range\n",
- filename, line_num);
- errors++;
+ ERROR("VideoQMin out of range\n");
}
}
} else if (!strcasecmp(cmd, "LumaElim")) {
} else if (!strcasecmp(cmd, "NoAudio")) {
audio_id = CODEC_ID_NONE;
} else if (!strcasecmp(cmd, "ACL")) {
- IPAddressACL acl;
-
- get_arg(arg, sizeof(arg), &p);
- if (strcasecmp(arg, "allow") == 0)
- acl.action = IP_ALLOW;
- else if (strcasecmp(arg, "deny") == 0)
- acl.action = IP_DENY;
- else {
- fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
- filename, line_num, arg);
- errors++;
- }
-
- get_arg(arg, sizeof(arg), &p);
-
- if (resolve_host(&acl.first, arg) != 0) {
- fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
- filename, line_num, arg);
- errors++;
- } else
- acl.last = acl.first;
-
- get_arg(arg, sizeof(arg), &p);
-
- if (arg[0]) {
- if (resolve_host(&acl.last, arg) != 0) {
- fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
- filename, line_num, arg);
- errors++;
- }
- }
-
- if (!errors) {
- IPAddressACL *nacl = av_mallocz(sizeof(*nacl));
- IPAddressACL **naclp = 0;
-
- acl.next = 0;
- *nacl = acl;
-
- if (stream)
- naclp = &stream->acl;
- else if (feed)
- naclp = &feed->acl;
- else {
- fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
- filename, line_num);
- errors++;
- }
-
- if (naclp) {
- while (*naclp)
- naclp = &(*naclp)->next;
-
- *naclp = nacl;
- }
+ parse_acl_row(stream, feed, NULL, p, filename, line_num);
+ } else if (!strcasecmp(cmd, "DynamicACL")) {
+ if (stream) {
+ get_arg(stream->dynamic_acl, sizeof(stream->dynamic_acl), &p);
}
} else if (!strcasecmp(cmd, "RTSPOption")) {
get_arg(arg, sizeof(arg), &p);
get_arg(arg, sizeof(arg), &p);
if (stream) {
if (resolve_host(&stream->multicast_ip, arg) != 0) {
- fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
- filename, line_num, arg);
- errors++;
+ ERROR("Invalid host/IP address: %s\n", arg);
}
stream->is_multicast = 1;
stream->loop = 1; /* default is looping */
stream->loop = 0;
} else if (!strcasecmp(cmd, "</Stream>")) {
if (!stream) {
- fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n",
- filename, line_num);
- errors++;
+ ERROR("No corresponding <Stream> for </Stream>\n");
} else {
if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
if (audio_id != CODEC_ID_NONE) {
- audio_enc.codec_type = CODEC_TYPE_AUDIO;
+ audio_enc.codec_type = AVMEDIA_TYPE_AUDIO;
audio_enc.codec_id = audio_id;
add_codec(stream, &audio_enc);
}
if (video_id != CODEC_ID_NONE) {
- video_enc.codec_type = CODEC_TYPE_VIDEO;
+ video_enc.codec_type = AVMEDIA_TYPE_VIDEO;
video_enc.codec_id = video_id;
add_codec(stream, &video_enc);
}
/*********************************************/
char *q;
if (stream || feed || redirect) {
- fprintf(stderr, "%s:%d: Already in a tag\n",
- filename, line_num);
- errors++;
+ ERROR("Already in a tag\n");
} else {
redirect = av_mallocz(sizeof(FFStream));
*last_stream = redirect;
get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
} else if (!strcasecmp(cmd, "</Redirect>")) {
if (!redirect) {
- fprintf(stderr, "%s:%d: No corresponding <Redirect> for </Redirect>\n",
- filename, line_num);
- errors++;
+ ERROR("No corresponding <Redirect> for </Redirect>\n");
} else {
if (!redirect->feed_filename[0]) {
- fprintf(stderr, "%s:%d: No URL found for <Redirect>\n",
- filename, line_num);
- errors++;
+ ERROR("No URL found for <Redirect>\n");
}
redirect = NULL;
}
#if HAVE_DLOPEN
load_module(arg);
#else
- fprintf(stderr, "%s:%d: Module support not compiled into this version: '%s'\n",
- filename, line_num, arg);
- errors++;
+ ERROR("Module support not compiled into this version: '%s'\n", arg);
#endif
} else {
- fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n",
- filename, line_num, cmd);
+ ERROR("Incorrect keyword: '%s'\n", cmd);
}
}
+#undef ERROR
fclose(f);
if (errors)
logfilename[0] = '-';
}
-static void opt_show_help(void)
+static void show_help(void)
{
printf("usage: ffserver [options]\n"
"Hyper fast multi format Audio/Video streaming server\n");
}
static const OptionDef options[] = {
- { "h", OPT_EXIT, {(void*)opt_show_help}, "show help" },
- { "version", OPT_EXIT, {(void*)show_version}, "show version" },
- { "L", OPT_EXIT, {(void*)show_license}, "show license" },
- { "formats" , OPT_EXIT, {(void*)show_formats }, "show available formats" },
- { "codecs" , OPT_EXIT, {(void*)show_codecs }, "show available codecs" },
- { "bsfs" , OPT_EXIT, {(void*)show_bsfs }, "show available bit stream filters" },
- { "protocols", OPT_EXIT, {(void*)show_protocols}, "show available protocols" },
- { "loglevel", HAS_ARG | OPT_FUNC2, {(void*)opt_loglevel}, "set libav* logging level", "loglevel" },
+#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" },
show_banner();
- config_filename = "/etc/ffserver.conf";
-
my_program_name = argv[0];
my_program_dir = getcwd(0, 0);
ffserver_daemon = 1;
unsetenv("http_proxy"); /* Kill the http_proxy */
- av_lfg_init(&random_state, ff_random_get_seed());
+ av_lfg_init(&random_state, av_get_random_seed());
memset(&sigact, 0, sizeof(sigact));
sigact.sa_handler = handle_child_exit;