diff --git a/ffserver.c b/ffserver.c index 1686fecbe2..e11f8763bb 100644 --- a/ffserver.c +++ b/ffserver.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -69,6 +70,11 @@ const char *http_state[] = { #define REQUEST_TIMEOUT (15 * 1000) #define SYNC_TIMEOUT (10 * 1000) +typedef struct { + INT64 count1, count2; + long time1, time2; +} DataRateData; + /* context associated with one connection */ typedef struct HTTPContext { enum HTTPState state; @@ -96,6 +102,7 @@ typedef struct HTTPContext { int suppress_log; int bandwidth; long start_time; /* In milliseconds - this wraps fairly often */ + DataRateData datarate; int wmp_client_id; char protocol[16]; char method[16]; @@ -132,6 +139,7 @@ typedef struct FFStream { char copyright[512]; char comment[512]; pid_t pid; /* Of ffmpeg process */ + time_t pid_start; /* Of ffmpeg process */ char **child_argv; struct FFStream *next; /* feed specific */ @@ -156,9 +164,9 @@ HTTPContext *first_http_ctx; FFStream *first_feed; /* contains only feeds */ FFStream *first_stream; /* contains all streams, including feeds */ -static int handle_http(HTTPContext *c, long cur_time); +static int handle_http(HTTPContext *c); static int http_parse_request(HTTPContext *c); -static int http_send_data(HTTPContext *c, long cur_time); +static int http_send_data(HTTPContext *c); static void compute_stats(HTTPContext *c); static int open_input_stream(HTTPContext *c, const char *info); static int http_start_receive_data(HTTPContext *c); @@ -168,6 +176,7 @@ static const char *my_program_name; static int ffserver_debug; static int no_launch; +static int need_to_start_children; int nb_max_connections; int nb_connections; @@ -175,6 +184,8 @@ int nb_connections; int nb_max_bandwidth; int nb_bandwidth; +static long cur_time; // Making this global saves on passing it around everywhere + static long gettime_ms(void) { struct timeval tv; @@ -218,13 +229,39 @@ static void log_connection(HTTPContext *c) buf1, buf2, c->method, c->url, c->protocol, (c->http_error ? c->http_error : 200), c->data_count); } +static void update_datarate(DataRateData *drd, INT64 count) +{ + if (!drd->time1 && !drd->count1) { + drd->time1 = drd->time2 = cur_time; + drd->count1 = drd->count2 = count; + } else { + if (cur_time - drd->time2 > 5000) { + drd->time1 = drd->time2; + drd->count1 = drd->count2; + drd->time2 = cur_time; + drd->count2 = count; + } + } +} + +/* In bytes per second */ +static int compute_datarate(DataRateData *drd, INT64 count) +{ + if (cur_time == drd->time1) + return 0; + + return ((count - drd->count1) * 1000) / (cur_time - drd->time1); +} + static void start_children(FFStream *feed) { if (no_launch) return; for (; feed; feed = feed->next) { - if (feed->child_argv) { + if (feed->child_argv && !feed->pid) { + feed->pid_start = time(0); + feed->pid = fork(); if (feed->pid < 0) { @@ -237,16 +274,18 @@ static void start_children(FFStream *feed) char *slash; int i; - if (!ffserver_debug) { - for (i = 0; i < 10; i++) { - close(i); - } + for (i = 3; i < 256; i++) { + close(i); + } + if (!ffserver_debug) { i = open("/dev/null", O_RDWR); if (i) dup2(i, 0); dup2(i, 1); dup2(i, 2); + if (i) + close(i); } pstrcpy(pathname, sizeof(pathname), my_program_name); @@ -274,7 +313,6 @@ static int http_server(struct sockaddr_in my_addr) struct sockaddr_in from_addr; struct pollfd poll_table[HTTP_MAX_CONNECTIONS + 1], *poll_entry; HTTPContext *c, **cp; - long cur_time; server_fd = socket(AF_INET,SOCK_STREAM,0); if (server_fd < 0) { @@ -360,12 +398,17 @@ static int http_server(struct sockaddr_in my_addr) cur_time = gettime_ms(); + if (need_to_start_children) { + need_to_start_children = 0; + start_children(first_feed); + } + /* now handle the events */ cp = &first_http_ctx; while ((*cp) != NULL) { c = *cp; - if (handle_http (c, cur_time) < 0) { + if (handle_http (c) < 0) { /* close and free the connection */ log_connection(c); close(c->fd); @@ -430,7 +473,7 @@ static int http_server(struct sockaddr_in my_addr) } } -static int handle_http(HTTPContext *c, long cur_time) +static int handle_http(HTTPContext *c) { int len; @@ -507,7 +550,7 @@ static int handle_http(HTTPContext *c, long cur_time) if (!(c->poll_entry->revents & POLLOUT)) return 0; - if (http_send_data(c, cur_time) < 0) + if (http_send_data(c) < 0) return -1; break; case HTTPSTATE_RECEIVE_DATA: @@ -1207,7 +1250,7 @@ static void compute_stats(HTTPContext *c) #ifdef linux /* This is somewhat linux specific I guess */ - snprintf(ps_cmd, sizeof(ps_cmd), "ps -o \"%%cpu,cputime\" --no-headers %d", stream->pid); + snprintf(ps_cmd, sizeof(ps_cmd), "ps -o \"%%cpu,bsdtime\" --no-headers %d", stream->pid); pid_stat = popen(ps_cmd, "r"); if (pid_stat) { @@ -1295,7 +1338,7 @@ static void compute_stats(HTTPContext *c) nb_bandwidth, nb_max_bandwidth); q += sprintf(q, "\n"); - q += sprintf(q, "
#FileIPStatekbits/secSize\n"); + q += sprintf(q, "
#FileIPStateTarget bits/secActual bits/secBytes transferred\n"); c1 = first_http_ctx; i = 0; while (c1 != NULL && q < (char *) c->buffer + c->buffer_size - 2048) { @@ -1311,12 +1354,15 @@ static void compute_stats(HTTPContext *c) i++; p = inet_ntoa(c1->from_addr.sin_addr); - q += sprintf(q, "
%d%s%s %s %s %d ", + q += sprintf(q, "
%d%s%s %s %s ", i, c1->stream->filename, c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "", p, - http_state[c1->state], - bitrate / 1000); + http_state[c1->state]); + q += fmt_bytecount(q, bitrate); + q += sprintf(q, ""); + q += fmt_bytecount(q, compute_datarate(&c1->datarate, c1->data_count) * 8); + q += sprintf(q, ""); q += fmt_bytecount(q, c1->data_count); *q++ = '\n'; c1 = c1->next; @@ -1414,7 +1460,7 @@ static int open_input_stream(HTTPContext *c, const char *info) return 0; } -static int http_prepare_data(HTTPContext *c, long cur_time) +static int http_prepare_data(HTTPContext *c) { int i; @@ -1622,12 +1668,12 @@ static int http_prepare_data(HTTPContext *c, long cur_time) } /* should convert the format at the same time */ -static int http_send_data(HTTPContext *c, long cur_time) +static int http_send_data(HTTPContext *c) { int len, ret; while (c->buffer_ptr >= c->buffer_end) { - ret = http_prepare_data(c, cur_time); + ret = http_prepare_data(c); if (ret < 0) return -1; else if (ret == 0) { @@ -1648,6 +1694,7 @@ static int http_send_data(HTTPContext *c, long cur_time) } else { c->buffer_ptr += len; c->data_count += len; + update_datarate(&c->datarate, c->data_count); if (c->stream) c->stream->bytes_served += len; } @@ -1698,6 +1745,7 @@ static int http_receive_data(HTTPContext *c) } else { c->buffer_ptr += len; c->data_count += len; + update_datarate(&c->datarate, c->data_count); } } @@ -2505,10 +2553,37 @@ void licence(void) ); } +static void handle_child_exit(int sig) +{ + pid_t pid; + int status; + + while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { + FFStream *feed; + + for (feed = 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); + + if (uptime < 30) { + /* Turn off any more restarts */ + feed->child_argv = 0; + } + } + } + } + + need_to_start_children = 1; +} + int main(int argc, char **argv) { const char *config_filename; int c; + struct sigaction sigact; register_all(); @@ -2553,6 +2628,11 @@ int main(int argc, char **argv) first_stream = NULL; logfilename[0] = '\0'; + memset(&sigact, 0, sizeof(sigact)); + sigact.sa_handler = handle_child_exit; + sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART; + sigaction(SIGCHLD, &sigact, 0); + if (parse_ffconfig(config_filename) < 0) { fprintf(stderr, "Incorrect config file - exiting.\n"); exit(1);