Commit a6e14edd authored by Philip Gladstone's avatar Philip Gladstone

* If a stream gets stuck in WAIT_FEED, then disconnecting the other end

  will clear out the stream. I think that this is really a linux bug in
  the handling of poll, but I did a workaround anyway.
* Improve the statistics output and prevent a buffer overrun when lots
  of clients are connected.
* Process ffm input when it is received and don't always be one ffm
  packet behind.
* Try to avoid going through the poll loop when not required.

Originally committed as revision 514 to svn://svn.ffmpeg.org/ffmpeg/trunk
parent 5e57424d
...@@ -125,6 +125,8 @@ typedef struct FFStream { ...@@ -125,6 +125,8 @@ typedef struct FFStream {
/* feed specific */ /* feed specific */
int feed_opened; /* true if someone if writing to feed */ int feed_opened; /* true if someone if writing to feed */
int is_feed; /* true if it is a feed */ int is_feed; /* true if it is a feed */
int conns_served;
INT64 bytes_served;
INT64 feed_max_size; /* maximum storage size */ INT64 feed_max_size; /* maximum storage size */
INT64 feed_write_index; /* current write position in feed (it wraps round) */ INT64 feed_write_index; /* current write position in feed (it wraps round) */
INT64 feed_size; /* current size of feed */ INT64 feed_size; /* current size of feed */
...@@ -272,7 +274,7 @@ static int http_server(struct sockaddr_in my_addr) ...@@ -272,7 +274,7 @@ static int http_server(struct sockaddr_in my_addr)
/* need to catch errors */ /* need to catch errors */
c->poll_entry = poll_entry; c->poll_entry = poll_entry;
poll_entry->fd = fd; poll_entry->fd = fd;
poll_entry->events = 0; poll_entry->events = POLLIN;/* Maybe this will work */
poll_entry++; poll_entry++;
break; break;
default: default:
...@@ -398,6 +400,8 @@ static int handle_http(HTTPContext *c, long cur_time) ...@@ -398,6 +400,8 @@ static int handle_http(HTTPContext *c, long cur_time)
} }
} else { } else {
c->buffer_ptr += len; c->buffer_ptr += len;
c->stream->bytes_served += len;
c->data_count += len;
if (c->buffer_ptr >= c->buffer_end) { if (c->buffer_ptr >= c->buffer_end) {
/* if error, exit */ /* if error, exit */
if (c->http_error) if (c->http_error)
...@@ -432,7 +436,7 @@ static int handle_http(HTTPContext *c, long cur_time) ...@@ -432,7 +436,7 @@ static int handle_http(HTTPContext *c, long cur_time)
break; break;
case HTTPSTATE_WAIT_FEED: case HTTPSTATE_WAIT_FEED:
/* no need to read if no events */ /* no need to read if no events */
if (c->poll_entry->revents & (POLLERR | POLLHUP)) if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
return -1; return -1;
/* nothing to do, we'll be waken up by incoming feed packets */ /* nothing to do, we'll be waken up by incoming feed packets */
...@@ -647,6 +651,7 @@ static int http_parse_request(HTTPContext *c) ...@@ -647,6 +651,7 @@ static int http_parse_request(HTTPContext *c)
} }
c->stream = stream; c->stream = stream;
stream->conns_served++;
/* XXX: add there authenticate and IP match */ /* XXX: add there authenticate and IP match */
...@@ -770,13 +775,14 @@ static void compute_stats(HTTPContext *c) ...@@ -770,13 +775,14 @@ static void compute_stats(HTTPContext *c)
q += sprintf(q, "<H1>FFServer Status</H1>\n"); q += sprintf(q, "<H1>FFServer Status</H1>\n");
/* format status */ /* format status */
q += sprintf(q, "<H2>Available Streams</H2>\n"); q += sprintf(q, "<H2>Available Streams</H2>\n");
q += sprintf(q, "<TABLE>\n"); q += sprintf(q, "<TABLE cellspacing=0 cellpadding=4>\n");
q += sprintf(q, "<TR><Th>Path<Th>Format<Th>Bit rate (kbits/s)<Th COLSPAN=2>Video<Th COLSPAN=2>Audio<Th align=left>Feed\n"); q += sprintf(q, "<TR><Th valign=top>Path<th align=left>Served<br>Conns<Th><br>kbytes<Th valign=top>Format<Th>Bit rate<br>kbits/s<Th align=left>Video<br>kbits/s<th><br>Codec<Th align=left>Audio<br>kbits/s<th><br>Codec<Th align=left valign=top>Feed\n");
stream = first_stream; stream = first_stream;
while (stream != NULL) { while (stream != NULL) {
char sfilename[1024]; char sfilename[1024];
char *eosf; char *eosf;
if (stream->feed != stream) {
strlcpy(sfilename, stream->filename, sizeof(sfilename) - 1); strlcpy(sfilename, stream->filename, sizeof(sfilename) - 1);
eosf = sfilename + strlen(sfilename); eosf = sfilename + strlen(sfilename);
if (eosf - sfilename >= 4) { if (eosf - sfilename >= 4) {
...@@ -789,6 +795,8 @@ static void compute_stats(HTTPContext *c) ...@@ -789,6 +795,8 @@ static void compute_stats(HTTPContext *c)
q += sprintf(q, "<TR><TD><A HREF=\"/%s\">%s</A> ", q += sprintf(q, "<TR><TD><A HREF=\"/%s\">%s</A> ",
sfilename, stream->filename); sfilename, stream->filename);
q += sprintf(q, "<td align=right> %d <td align=right> %lld",
stream->conns_served, stream->bytes_served / 1000);
switch(stream->stream_type) { switch(stream->stream_type) {
case STREAM_TYPE_LIVE: case STREAM_TYPE_LIVE:
{ {
...@@ -823,7 +831,7 @@ static void compute_stats(HTTPContext *c) ...@@ -823,7 +831,7 @@ static void compute_stats(HTTPContext *c)
abort(); abort();
} }
} }
q += sprintf(q, "<TD> %s <TD> %d <TD> %d <TD> %s %s <TD> %d <TD> %s %s", q += sprintf(q, "<TD align=center> %s <TD align=right> %d <TD align=right> %d <TD> %s %s <TD align=right> %d <TD> %s %s",
stream->fmt->name, stream->fmt->name,
(audio_bit_rate + video_bit_rate) / 1000, (audio_bit_rate + video_bit_rate) / 1000,
video_bit_rate / 1000, video_codec_name, video_codec_name_extra, video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
...@@ -837,13 +845,44 @@ static void compute_stats(HTTPContext *c) ...@@ -837,13 +845,44 @@ static void compute_stats(HTTPContext *c)
} }
break; break;
default: default:
q += sprintf(q, "<TD> - <TD> - <TD COLSPAN=2> - <TD COLSPAN=2> -\n"); q += sprintf(q, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
break; break;
} }
}
stream = stream->next; stream = stream->next;
} }
q += sprintf(q, "</TABLE>\n"); q += sprintf(q, "</TABLE>\n");
stream = first_stream;
while (stream != NULL) {
if (stream->feed == stream) {
q += sprintf(q, "<h2>Feed %s</h2>", stream->filename);
q += sprintf(q, "<table cellspacing=0 cellpadding=4><tr><th>Stream<th>type<th>kbits/s<th align=left>codec\n");
for (i = 0; i < stream->nb_streams; i++) {
AVStream *st = stream->streams[i];
AVCodec *codec = avcodec_find_encoder(st->codec.codec_id);
char *type = "unknown";
switch(st->codec.codec_type) {
case CODEC_TYPE_AUDIO:
type = "audio";
break;
case CODEC_TYPE_VIDEO:
type = "video";
break;
default:
abort();
}
q += sprintf(q, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s\n",
i, type, st->codec.bit_rate/1000, codec ? codec->name : "");
}
q += sprintf(q, "</table>\n");
}
stream = stream->next;
}
#if 0 #if 0
{ {
float avg; float avg;
...@@ -887,7 +926,7 @@ static void compute_stats(HTTPContext *c) ...@@ -887,7 +926,7 @@ static void compute_stats(HTTPContext *c)
q += sprintf(q, "<TR><TD>#<TD>File<TD>IP<TD>State<TD>Size\n"); q += sprintf(q, "<TR><TD>#<TD>File<TD>IP<TD>State<TD>Size\n");
c1 = first_http_ctx; c1 = first_http_ctx;
i = 0; i = 0;
while (c1 != NULL) { while (c1 != NULL && q < (char *) c->buffer + sizeof(c->buffer) - 2048) {
i++; i++;
p = inet_ntoa(c1->from_addr.sin_addr); p = inet_ntoa(c1->from_addr.sin_addr);
q += sprintf(q, "<TR><TD><B>%d</B><TD>%s%s <TD> %s <TD> %s <TD> %Ld\n", q += sprintf(q, "<TR><TD><B>%d</B><TD>%s%s <TD> %s <TD> %s <TD> %Ld\n",
...@@ -903,7 +942,7 @@ static void compute_stats(HTTPContext *c) ...@@ -903,7 +942,7 @@ static void compute_stats(HTTPContext *c)
/* date */ /* date */
ti = time(NULL); ti = time(NULL);
p = ctime(&ti); p = ctime(&ti);
q += sprintf(q, "<HR>Generated at %s", p); q += sprintf(q, "<HR size=1 noshade>Generated at %s", p);
q += sprintf(q, "</BODY>\n</HTML>\n"); q += sprintf(q, "</BODY>\n</HTML>\n");
c->buffer_ptr = c->buffer; c->buffer_ptr = c->buffer;
...@@ -1088,6 +1127,7 @@ static int http_prepare_data(HTTPContext *c) ...@@ -1088,6 +1127,7 @@ static int http_prepare_data(HTTPContext *c)
c->stream->feed->feed_write_index, c->stream->feed->feed_write_index,
c->stream->feed->feed_size); c->stream->feed->feed_size);
} }
if (av_read_packet(c->fmt_in, &pkt) < 0) { if (av_read_packet(c->fmt_in, &pkt) < 0) {
if (c->stream->feed && c->stream->feed->feed_opened) { if (c->stream->feed && c->stream->feed->feed_opened) {
/* if coming from feed, it means we reached the end of the /* if coming from feed, it means we reached the end of the
...@@ -1168,7 +1208,7 @@ static int http_send_data(HTTPContext *c) ...@@ -1168,7 +1208,7 @@ static int http_send_data(HTTPContext *c)
if (ret < 0) if (ret < 0)
return -1; return -1;
else if (ret == 0) { else if (ret == 0) {
break; continue;
} else { } else {
/* state change requested */ /* state change requested */
return 0; return 0;
...@@ -1185,6 +1225,7 @@ static int http_send_data(HTTPContext *c) ...@@ -1185,6 +1225,7 @@ static int http_send_data(HTTPContext *c)
} else { } else {
c->buffer_ptr += len; c->buffer_ptr += len;
c->data_count += len; c->data_count += len;
c->stream->bytes_served += len;
} }
} }
return 0; return 0;
...@@ -1216,9 +1257,26 @@ static int http_start_receive_data(HTTPContext *c) ...@@ -1216,9 +1257,26 @@ static int http_start_receive_data(HTTPContext *c)
static int http_receive_data(HTTPContext *c) static int http_receive_data(HTTPContext *c)
{ {
int len;
HTTPContext *c1; HTTPContext *c1;
if (c->buffer_end > c->buffer_ptr) {
int len;
len = read(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
if (len < 0) {
if (errno != EAGAIN && errno != EINTR) {
/* error : close connection */
goto fail;
}
} else if (len == 0) {
/* end of connection : close it */
goto fail;
} else {
c->buffer_ptr += len;
c->data_count += len;
}
}
if (c->buffer_ptr >= c->buffer_end) { if (c->buffer_ptr >= c->buffer_end) {
FFStream *feed = c->stream; FFStream *feed = c->stream;
/* a packet has been received : write it in the store, except /* a packet has been received : write it in the store, except
...@@ -1276,19 +1334,6 @@ static int http_receive_data(HTTPContext *c) ...@@ -1276,19 +1334,6 @@ static int http_receive_data(HTTPContext *c)
c->buffer_ptr = c->buffer; c->buffer_ptr = c->buffer;
} }
len = read(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
if (len < 0) {
if (errno != EAGAIN && errno != EINTR) {
/* error : close connection */
goto fail;
}
} else if (len == 0) {
/* end of connection : close it */
goto fail;
} else {
c->buffer_ptr += len;
c->data_count += len;
}
return 0; return 0;
fail: fail:
c->stream->feed_opened = 0; c->stream->feed_opened = 0;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment