STREAM_TYPE_REDIRECT,
};
+enum IPAddressAction {
+ IP_ALLOW = 1,
+ IP_DENY,
+};
+
+typedef struct IPAddressACL {
+ struct IPAddressACL *next;
+ enum IPAddressAction action;
+ struct in_addr first;
+ struct in_addr last;
+} IPAddressACL;
+
/* description of each stream of the ffserver.conf file */
typedef struct FFStream {
enum StreamType stream_type;
struct FFStream *feed; /* feed we are using (can be null if
coming from file) */
AVOutputFormat *fmt;
+ IPAddressACL *acl;
int nb_streams;
int prebuffer; /* Number of millseconds early to start */
long max_time; /* Number of milliseconds to run */
return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
}
+static int get_longterm_datarate(DataRateData *drd, INT64 count)
+{
+ /* You get the first 3 seconds flat out */
+ if (cur_time - drd->time1 < 3000)
+ return 0;
+
+ return compute_datarate(drd, count);
+}
+
+
static void start_children(FFStream *feed)
{
if (no_launch)
*pp = p;
}
+static int validate_acl(FFStream *stream, HTTPContext *c)
+{
+ enum IPAddressAction last_action = IP_DENY;
+ IPAddressACL *acl;
+ struct in_addr *src = &c->from_addr.sin_addr;
+
+ for (acl = stream->acl; acl; acl = acl->next) {
+ if (src->s_addr >= acl->first.s_addr && src->s_addr <= acl->last.s_addr) {
+ return (acl->action == IP_ALLOW) ? 1 : 0;
+ }
+ last_action = acl->action;
+ }
+
+ /* Nothing matched, so return not the last action */
+ return (last_action == IP_DENY) ? 1 : 0;
+}
+
/* parse http request and prepare header */
static int http_parse_request(HTTPContext *c)
{
stream = first_stream;
while (stream != NULL) {
- if (!strcmp(stream->filename, filename))
+ if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
break;
stream = stream->next;
}
q += sprintf(q, "Pragma: no-cache\r\n");
/* for asf, we need extra headers */
- if (!strcmp(c->stream->fmt->name,"asf")) {
+ if (!strcmp(c->stream->fmt->name,"asf_stream")) {
/* Need to allocate a client id */
- static int wmp_session;
- if (!wmp_session)
- wmp_session = time(0) & 0xffffff;
-
- c->wmp_client_id = ++wmp_session;
+ c->wmp_client_id = random() & 0x7fffffff;
q += sprintf(q, "Server: Cougar 4.1.0.3923\r\nCache-Control: no-cache\r\nPragma: client-id=%d\r\nPragma: features=\"broadcast\"\r\n", c->wmp_client_id);
mime_type = "application/octet-stream";
stream_pos = parse_date(buf, 0);
} else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
int prebuffer = strtol(buf, 0, 10);
- stream_pos = av_gettime() - prebuffer * 1000000;
+ stream_pos = av_gettime() - prebuffer * (INT64)1000000;
} else {
- stream_pos = av_gettime() - c->stream->prebuffer * 1000;
+ stream_pos = av_gettime() - c->stream->prebuffer * (INT64)1000;
}
} else {
strcpy(input_filename, c->stream->feed_filename);
if (input_filename[0] == '\0')
return -1;
+#if 0
+ { time_t when = stream_pos / 1000000;
+ http_log("Stream pos = %lld, time=%s", stream_pos, ctime(&when));
+ }
+#endif
+
/* open stream */
if (av_open_input_file(&s, input_filename, NULL, buf_size, NULL) < 0) {
http_log("%s not found", input_filename);
/* set the start time (needed for maxtime and RTP packet timing) */
c->start_time = cur_time;
c->first_pts = AV_NOPTS_VALUE;
- printf("stream %s opened pos=%0.6f\n", input_filename, stream_pos / 1000000.0);
return 0;
}
static int compute_send_delay(HTTPContext *c)
{
+ int datarate = 8 * get_longterm_datarate(&c->datarate, c->data_count);
+
+ if (datarate > c->bandwidth * 2000) {
+ return 1000;
+ }
return 0;
}
/* We have timed out */
c->state = HTTPSTATE_SEND_DATA_TRAILER;
} else {
- if (c->is_packetized) {
+ if (1 || c->is_packetized) {
if (compute_send_delay(c) > 0) {
c->state = HTTPSTATE_WAIT;
return 1; /* state changed */
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\n", i);
+ matches = 0;
+ } else {
+ AVCodecContext *ccf, *ccs;
+
+ 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(frame_rate) ||
+ 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);
+ }
+ if (!matches)
+ unlink(feed->feed_filename);
+ }
if (!url_exist(feed->feed_filename)) {
AVFormatContext s1, *s = &s1;
av->qcompress = 0.5;
av->qblur = 0.5;
+ if (!av->rc_eq)
+ av->rc_eq = "tex^qComp";
+ if (!av->i_quant_factor)
+ 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;
+
+
break;
default:
av_abort();
fprintf(stderr, "%s:%d: No corresponding <Feed> for </Feed>\n",
filename, line_num);
errors++;
+#if 0
} else {
/* Make sure that we start out clean */
if (unlink(feed->feed_filename) < 0
filename, line_num, feed->feed_filename, strerror(errno));
errors++;
}
+#endif
}
feed = NULL;
} else if (!strcasecmp(cmd, "<Stream")) {
q = strrchr(stream->filename, '>');
if (*q)
*q = '\0';
- stream->fmt = guess_format(NULL, stream->filename, NULL);
+ 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;
/* jpeg cannot be used here, so use single frame jpeg */
if (!strcmp(arg, "jpeg"))
strcpy(arg, "singlejpeg");
- stream->fmt = guess_format(arg, NULL, NULL);
+ stream->fmt = guess_stream_format(arg, NULL, NULL);
if (!stream->fmt) {
fprintf(stderr, "%s:%d: Unknown Format: %s\n",
filename, line_num, arg);
} else if (!strcasecmp(cmd, "Preroll")) {
get_arg(arg, sizeof(arg), &p);
if (stream) {
- stream->prebuffer = atoi(arg) * 1000;
+ stream->prebuffer = atof(arg) * 1000;
}
} else if (!strcasecmp(cmd, "StartSendOnKey")) {
if (stream) {
} else if (!strcasecmp(cmd, "MaxTime")) {
get_arg(arg, sizeof(arg), &p);
if (stream) {
- stream->max_time = atoi(arg) * 1000;
+ stream->max_time = atof(arg) * 1000;
}
} else if (!strcasecmp(cmd, "AudioBitRate")) {
get_arg(arg, sizeof(arg), &p);
if (stream) {
audio_enc.sample_rate = atoi(arg);
}
+ } else if (!strcasecmp(cmd, "AudioQuality")) {
+ get_arg(arg, sizeof(arg), &p);
+ if (stream) {
+ audio_enc.quality = atof(arg) * 1000;
+ }
} else if (!strcasecmp(cmd, "VideoBitRate")) {
get_arg(arg, sizeof(arg), &p);
if (stream) {
video_enc.flags |= CODEC_FLAG_HQ;
}
} else if (!strcasecmp(cmd, "VideoQDiff")) {
+ get_arg(arg, sizeof(arg), &p);
if (stream) {
video_enc.max_qdiff = atoi(arg);
if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
}
}
} else if (!strcasecmp(cmd, "VideoQMax")) {
+ get_arg(arg, sizeof(arg), &p);
if (stream) {
video_enc.qmax = atoi(arg);
if (video_enc.qmax < 1 || video_enc.qmax > 31) {
}
}
} else if (!strcasecmp(cmd, "VideoQMin")) {
+ get_arg(arg, sizeof(arg), &p);
if (stream) {
video_enc.qmin = atoi(arg);
if (video_enc.qmin < 1 || video_enc.qmin > 31) {
video_id = CODEC_ID_NONE;
} else if (!strcasecmp(cmd, "NoAudio")) {
audio_id = CODEC_ID_NONE;
+ } else if (!strcasecmp(cmd, "ACL")) {
+ IPAddressACL acl;
+ struct hostent *he;
+
+ 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);
+
+ he = gethostbyname(arg);
+ if (!he) {
+ fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
+ filename, line_num, arg);
+ errors++;
+ } else {
+ /* Only take the first */
+ acl.first = *(struct in_addr *) he->h_addr_list[0];
+ acl.last = acl.first;
+ }
+
+ get_arg(arg, sizeof(arg), &p);
+
+ if (arg[0]) {
+ he = gethostbyname(arg);
+ if (!he) {
+ fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
+ filename, line_num, arg);
+ errors++;
+ } else {
+ /* Only take the first */
+ acl.last = *(struct in_addr *) he->h_addr_list[0];
+ }
+ }
+
+ if (!errors) {
+ IPAddressACL *nacl = (IPAddressACL *) av_mallocz(sizeof(*nacl));
+ IPAddressACL **naclp = 0;
+
+ *nacl = acl;
+ nacl->next = 0;
+
+ 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;
+ }
+ }
} else if (!strcasecmp(cmd, "RTSPOption")) {
get_arg(arg, sizeof(arg), &p);
if (stream) {
putenv("http_proxy"); /* Kill the http_proxy */
+ srandom(gettime_ms() + (getpid() << 16));
+
/* address on which the server will handle HTTP connections */
my_http_addr.sin_family = AF_INET;
my_http_addr.sin_port = htons (8080);
setsid();
chdir("/");
close(0);
- close(1);
- close(2);
open("/dev/null", O_RDWR);
- dup(0);
+ if (strcmp(logfilename, "-") != 0) {
+ close(1);
+ dup(0);
+ }
+ close(2);
dup(0);
}
}