Commit 5eb765ef authored by Philip Gladstone's avatar Philip Gladstone

* Add code to restart ffmpeg if it crashes

* Add code to monitor the actual datarates on the http connections
* Fix problem when ffmpeg uses more than 24 hours of CPU (display only problem)

Originally committed as revision 680 to svn://svn.ffmpeg.org/ffmpeg/trunk
parent 6282185e
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include <getopt.h> #include <getopt.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/wait.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <netdb.h> #include <netdb.h>
#include <ctype.h> #include <ctype.h>
...@@ -69,6 +70,11 @@ const char *http_state[] = { ...@@ -69,6 +70,11 @@ const char *http_state[] = {
#define REQUEST_TIMEOUT (15 * 1000) #define REQUEST_TIMEOUT (15 * 1000)
#define SYNC_TIMEOUT (10 * 1000) #define SYNC_TIMEOUT (10 * 1000)
typedef struct {
INT64 count1, count2;
long time1, time2;
} DataRateData;
/* context associated with one connection */ /* context associated with one connection */
typedef struct HTTPContext { typedef struct HTTPContext {
enum HTTPState state; enum HTTPState state;
...@@ -96,6 +102,7 @@ typedef struct HTTPContext { ...@@ -96,6 +102,7 @@ typedef struct HTTPContext {
int suppress_log; int suppress_log;
int bandwidth; int bandwidth;
long start_time; /* In milliseconds - this wraps fairly often */ long start_time; /* In milliseconds - this wraps fairly often */
DataRateData datarate;
int wmp_client_id; int wmp_client_id;
char protocol[16]; char protocol[16];
char method[16]; char method[16];
...@@ -132,6 +139,7 @@ typedef struct FFStream { ...@@ -132,6 +139,7 @@ typedef struct FFStream {
char copyright[512]; char copyright[512];
char comment[512]; char comment[512];
pid_t pid; /* Of ffmpeg process */ pid_t pid; /* Of ffmpeg process */
time_t pid_start; /* Of ffmpeg process */
char **child_argv; char **child_argv;
struct FFStream *next; struct FFStream *next;
/* feed specific */ /* feed specific */
...@@ -156,9 +164,9 @@ HTTPContext *first_http_ctx; ...@@ -156,9 +164,9 @@ HTTPContext *first_http_ctx;
FFStream *first_feed; /* contains only feeds */ FFStream *first_feed; /* contains only feeds */
FFStream *first_stream; /* contains all streams, including 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_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 void compute_stats(HTTPContext *c);
static int open_input_stream(HTTPContext *c, const char *info); static int open_input_stream(HTTPContext *c, const char *info);
static int http_start_receive_data(HTTPContext *c); static int http_start_receive_data(HTTPContext *c);
...@@ -168,6 +176,7 @@ static const char *my_program_name; ...@@ -168,6 +176,7 @@ static const char *my_program_name;
static int ffserver_debug; static int ffserver_debug;
static int no_launch; static int no_launch;
static int need_to_start_children;
int nb_max_connections; int nb_max_connections;
int nb_connections; int nb_connections;
...@@ -175,6 +184,8 @@ int nb_connections; ...@@ -175,6 +184,8 @@ int nb_connections;
int nb_max_bandwidth; int nb_max_bandwidth;
int nb_bandwidth; int nb_bandwidth;
static long cur_time; // Making this global saves on passing it around everywhere
static long gettime_ms(void) static long gettime_ms(void)
{ {
struct timeval tv; struct timeval tv;
...@@ -218,13 +229,39 @@ static void log_connection(HTTPContext *c) ...@@ -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); 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) static void start_children(FFStream *feed)
{ {
if (no_launch) if (no_launch)
return; return;
for (; feed; feed = feed->next) { for (; feed; feed = feed->next) {
if (feed->child_argv) { if (feed->child_argv && !feed->pid) {
feed->pid_start = time(0);
feed->pid = fork(); feed->pid = fork();
if (feed->pid < 0) { if (feed->pid < 0) {
...@@ -237,16 +274,18 @@ static void start_children(FFStream *feed) ...@@ -237,16 +274,18 @@ static void start_children(FFStream *feed)
char *slash; char *slash;
int i; int i;
if (!ffserver_debug) { for (i = 3; i < 256; i++) {
for (i = 0; i < 10; i++) { close(i);
close(i); }
}
if (!ffserver_debug) {
i = open("/dev/null", O_RDWR); i = open("/dev/null", O_RDWR);
if (i) if (i)
dup2(i, 0); dup2(i, 0);
dup2(i, 1); dup2(i, 1);
dup2(i, 2); dup2(i, 2);
if (i)
close(i);
} }
pstrcpy(pathname, sizeof(pathname), my_program_name); pstrcpy(pathname, sizeof(pathname), my_program_name);
...@@ -274,7 +313,6 @@ static int http_server(struct sockaddr_in my_addr) ...@@ -274,7 +313,6 @@ static int http_server(struct sockaddr_in my_addr)
struct sockaddr_in from_addr; struct sockaddr_in from_addr;
struct pollfd poll_table[HTTP_MAX_CONNECTIONS + 1], *poll_entry; struct pollfd poll_table[HTTP_MAX_CONNECTIONS + 1], *poll_entry;
HTTPContext *c, **cp; HTTPContext *c, **cp;
long cur_time;
server_fd = socket(AF_INET,SOCK_STREAM,0); server_fd = socket(AF_INET,SOCK_STREAM,0);
if (server_fd < 0) { if (server_fd < 0) {
...@@ -360,12 +398,17 @@ static int http_server(struct sockaddr_in my_addr) ...@@ -360,12 +398,17 @@ static int http_server(struct sockaddr_in my_addr)
cur_time = gettime_ms(); cur_time = gettime_ms();
if (need_to_start_children) {
need_to_start_children = 0;
start_children(first_feed);
}
/* now handle the events */ /* now handle the events */
cp = &first_http_ctx; cp = &first_http_ctx;
while ((*cp) != NULL) { while ((*cp) != NULL) {
c = *cp; c = *cp;
if (handle_http (c, cur_time) < 0) { if (handle_http (c) < 0) {
/* close and free the connection */ /* close and free the connection */
log_connection(c); log_connection(c);
close(c->fd); close(c->fd);
...@@ -430,7 +473,7 @@ static int http_server(struct sockaddr_in my_addr) ...@@ -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; int len;
...@@ -507,7 +550,7 @@ static int handle_http(HTTPContext *c, long cur_time) ...@@ -507,7 +550,7 @@ static int handle_http(HTTPContext *c, long cur_time)
if (!(c->poll_entry->revents & POLLOUT)) if (!(c->poll_entry->revents & POLLOUT))
return 0; return 0;
if (http_send_data(c, cur_time) < 0) if (http_send_data(c) < 0)
return -1; return -1;
break; break;
case HTTPSTATE_RECEIVE_DATA: case HTTPSTATE_RECEIVE_DATA:
...@@ -1207,7 +1250,7 @@ static void compute_stats(HTTPContext *c) ...@@ -1207,7 +1250,7 @@ static void compute_stats(HTTPContext *c)
#ifdef linux #ifdef linux
/* This is somewhat linux specific I guess */ /* 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"); pid_stat = popen(ps_cmd, "r");
if (pid_stat) { if (pid_stat) {
...@@ -1295,7 +1338,7 @@ static void compute_stats(HTTPContext *c) ...@@ -1295,7 +1338,7 @@ static void compute_stats(HTTPContext *c)
nb_bandwidth, nb_max_bandwidth); nb_bandwidth, nb_max_bandwidth);
q += sprintf(q, "<TABLE>\n"); q += sprintf(q, "<TABLE>\n");
q += sprintf(q, "<TR><Th>#<Th>File<Th>IP<Th>State<Th>kbits/sec<Th>Size\n"); q += sprintf(q, "<TR><Th>#<Th>File<Th>IP<Th>State<Th>Target bits/sec<Th>Actual bits/sec<Th>Bytes transferred\n");
c1 = first_http_ctx; c1 = first_http_ctx;
i = 0; i = 0;
while (c1 != NULL && q < (char *) c->buffer + c->buffer_size - 2048) { while (c1 != NULL && q < (char *) c->buffer + c->buffer_size - 2048) {
...@@ -1311,12 +1354,15 @@ static void compute_stats(HTTPContext *c) ...@@ -1311,12 +1354,15 @@ static void compute_stats(HTTPContext *c)
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 align=right> %d <TD align=right> ", q += sprintf(q, "<TR><TD><B>%d</B><TD>%s%s <TD> %s <TD> %s <td align=right>",
i, c1->stream->filename, i, c1->stream->filename,
c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "", c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
p, p,
http_state[c1->state], http_state[c1->state]);
bitrate / 1000); q += fmt_bytecount(q, bitrate);
q += sprintf(q, "<td align=right>");
q += fmt_bytecount(q, compute_datarate(&c1->datarate, c1->data_count) * 8);
q += sprintf(q, "<td align=right>");
q += fmt_bytecount(q, c1->data_count); q += fmt_bytecount(q, c1->data_count);
*q++ = '\n'; *q++ = '\n';
c1 = c1->next; c1 = c1->next;
...@@ -1414,7 +1460,7 @@ static int open_input_stream(HTTPContext *c, const char *info) ...@@ -1414,7 +1460,7 @@ static int open_input_stream(HTTPContext *c, const char *info)
return 0; return 0;
} }
static int http_prepare_data(HTTPContext *c, long cur_time) static int http_prepare_data(HTTPContext *c)
{ {
int i; int i;
...@@ -1622,12 +1668,12 @@ static int http_prepare_data(HTTPContext *c, long cur_time) ...@@ -1622,12 +1668,12 @@ static int http_prepare_data(HTTPContext *c, long cur_time)
} }
/* should convert the format at the same 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; int len, ret;
while (c->buffer_ptr >= c->buffer_end) { while (c->buffer_ptr >= c->buffer_end) {
ret = http_prepare_data(c, cur_time); ret = http_prepare_data(c);
if (ret < 0) if (ret < 0)
return -1; return -1;
else if (ret == 0) { else if (ret == 0) {
...@@ -1648,6 +1694,7 @@ static int http_send_data(HTTPContext *c, long cur_time) ...@@ -1648,6 +1694,7 @@ static int http_send_data(HTTPContext *c, long cur_time)
} else { } else {
c->buffer_ptr += len; c->buffer_ptr += len;
c->data_count += len; c->data_count += len;
update_datarate(&c->datarate, c->data_count);
if (c->stream) if (c->stream)
c->stream->bytes_served += len; c->stream->bytes_served += len;
} }
...@@ -1698,6 +1745,7 @@ static int http_receive_data(HTTPContext *c) ...@@ -1698,6 +1745,7 @@ static int http_receive_data(HTTPContext *c)
} else { } else {
c->buffer_ptr += len; c->buffer_ptr += len;
c->data_count += len; c->data_count += len;
update_datarate(&c->datarate, c->data_count);
} }
} }
...@@ -2505,10 +2553,37 @@ void licence(void) ...@@ -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) int main(int argc, char **argv)
{ {
const char *config_filename; const char *config_filename;
int c; int c;
struct sigaction sigact;
register_all(); register_all();
...@@ -2553,6 +2628,11 @@ int main(int argc, char **argv) ...@@ -2553,6 +2628,11 @@ int main(int argc, char **argv)
first_stream = NULL; first_stream = NULL;
logfilename[0] = '\0'; 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) { if (parse_ffconfig(config_filename) < 0) {
fprintf(stderr, "Incorrect config file - exiting.\n"); fprintf(stderr, "Incorrect config file - exiting.\n");
exit(1); exit(1);
......
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