* 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
This commit is contained in:
118
ffserver.c
118
ffserver.c
@ -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[] = {
|
|||||||
#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 {
|
|||||||
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 {
|
|||||||
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;
|
|||||||
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;
|
|||||||
|
|
||||||
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;
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
|
|
||||||
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)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
|||||||
|
|
||||||
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)
|
|||||||
|
|
||||||
#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)
|
|||||||
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)
|
|||||||
|
|
||||||
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)
|
|||||||
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)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* 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)
|
|||||||
} 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)
|
|||||||
} 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)
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
|||||||
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);
|
||||||
|
Reference in New Issue
Block a user