Originally committed as revision 680 to svn://svn.ffmpeg.org/ffmpeg/trunk
Philip Gladstone authored on 2002/06/10 11:44:36... | ... |
@@ -31,6 +31,7 @@ |
31 | 31 |
#include <getopt.h> |
32 | 32 |
#include <sys/types.h> |
33 | 33 |
#include <sys/socket.h> |
34 |
+#include <sys/wait.h> |
|
34 | 35 |
#include <arpa/inet.h> |
35 | 36 |
#include <netdb.h> |
36 | 37 |
#include <ctype.h> |
... | ... |
@@ -69,6 +70,11 @@ const char *http_state[] = { |
69 | 69 |
#define REQUEST_TIMEOUT (15 * 1000) |
70 | 70 |
#define SYNC_TIMEOUT (10 * 1000) |
71 | 71 |
|
72 |
+typedef struct { |
|
73 |
+ INT64 count1, count2; |
|
74 |
+ long time1, time2; |
|
75 |
+} DataRateData; |
|
76 |
+ |
|
72 | 77 |
/* context associated with one connection */ |
73 | 78 |
typedef struct HTTPContext { |
74 | 79 |
enum HTTPState state; |
... | ... |
@@ -96,6 +102,7 @@ typedef struct HTTPContext { |
96 | 96 |
int suppress_log; |
97 | 97 |
int bandwidth; |
98 | 98 |
long start_time; /* In milliseconds - this wraps fairly often */ |
99 |
+ DataRateData datarate; |
|
99 | 100 |
int wmp_client_id; |
100 | 101 |
char protocol[16]; |
101 | 102 |
char method[16]; |
... | ... |
@@ -132,6 +139,7 @@ typedef struct FFStream { |
132 | 132 |
char copyright[512]; |
133 | 133 |
char comment[512]; |
134 | 134 |
pid_t pid; /* Of ffmpeg process */ |
135 |
+ time_t pid_start; /* Of ffmpeg process */ |
|
135 | 136 |
char **child_argv; |
136 | 137 |
struct FFStream *next; |
137 | 138 |
/* feed specific */ |
... | ... |
@@ -156,9 +164,9 @@ HTTPContext *first_http_ctx; |
156 | 156 |
FFStream *first_feed; /* contains only feeds */ |
157 | 157 |
FFStream *first_stream; /* contains all streams, including feeds */ |
158 | 158 |
|
159 |
-static int handle_http(HTTPContext *c, long cur_time); |
|
159 |
+static int handle_http(HTTPContext *c); |
|
160 | 160 |
static int http_parse_request(HTTPContext *c); |
161 |
-static int http_send_data(HTTPContext *c, long cur_time); |
|
161 |
+static int http_send_data(HTTPContext *c); |
|
162 | 162 |
static void compute_stats(HTTPContext *c); |
163 | 163 |
static int open_input_stream(HTTPContext *c, const char *info); |
164 | 164 |
static int http_start_receive_data(HTTPContext *c); |
... | ... |
@@ -168,6 +176,7 @@ static const char *my_program_name; |
168 | 168 |
|
169 | 169 |
static int ffserver_debug; |
170 | 170 |
static int no_launch; |
171 |
+static int need_to_start_children; |
|
171 | 172 |
|
172 | 173 |
int nb_max_connections; |
173 | 174 |
int nb_connections; |
... | ... |
@@ -175,6 +184,8 @@ int nb_connections; |
175 | 175 |
int nb_max_bandwidth; |
176 | 176 |
int nb_bandwidth; |
177 | 177 |
|
178 |
+static long cur_time; // Making this global saves on passing it around everywhere |
|
179 |
+ |
|
178 | 180 |
static long gettime_ms(void) |
179 | 181 |
{ |
180 | 182 |
struct timeval tv; |
... | ... |
@@ -218,13 +229,39 @@ static void log_connection(HTTPContext *c) |
218 | 218 |
buf1, buf2, c->method, c->url, c->protocol, (c->http_error ? c->http_error : 200), c->data_count); |
219 | 219 |
} |
220 | 220 |
|
221 |
+static void update_datarate(DataRateData *drd, INT64 count) |
|
222 |
+{ |
|
223 |
+ if (!drd->time1 && !drd->count1) { |
|
224 |
+ drd->time1 = drd->time2 = cur_time; |
|
225 |
+ drd->count1 = drd->count2 = count; |
|
226 |
+ } else { |
|
227 |
+ if (cur_time - drd->time2 > 5000) { |
|
228 |
+ drd->time1 = drd->time2; |
|
229 |
+ drd->count1 = drd->count2; |
|
230 |
+ drd->time2 = cur_time; |
|
231 |
+ drd->count2 = count; |
|
232 |
+ } |
|
233 |
+ } |
|
234 |
+} |
|
235 |
+ |
|
236 |
+/* In bytes per second */ |
|
237 |
+static int compute_datarate(DataRateData *drd, INT64 count) |
|
238 |
+{ |
|
239 |
+ if (cur_time == drd->time1) |
|
240 |
+ return 0; |
|
241 |
+ |
|
242 |
+ return ((count - drd->count1) * 1000) / (cur_time - drd->time1); |
|
243 |
+} |
|
244 |
+ |
|
221 | 245 |
static void start_children(FFStream *feed) |
222 | 246 |
{ |
223 | 247 |
if (no_launch) |
224 | 248 |
return; |
225 | 249 |
|
226 | 250 |
for (; feed; feed = feed->next) { |
227 |
- if (feed->child_argv) { |
|
251 |
+ if (feed->child_argv && !feed->pid) { |
|
252 |
+ feed->pid_start = time(0); |
|
253 |
+ |
|
228 | 254 |
feed->pid = fork(); |
229 | 255 |
|
230 | 256 |
if (feed->pid < 0) { |
... | ... |
@@ -237,16 +274,18 @@ static void start_children(FFStream *feed) |
237 | 237 |
char *slash; |
238 | 238 |
int i; |
239 | 239 |
|
240 |
- if (!ffserver_debug) { |
|
241 |
- for (i = 0; i < 10; i++) { |
|
242 |
- close(i); |
|
243 |
- } |
|
240 |
+ for (i = 3; i < 256; i++) { |
|
241 |
+ close(i); |
|
242 |
+ } |
|
244 | 243 |
|
244 |
+ if (!ffserver_debug) { |
|
245 | 245 |
i = open("/dev/null", O_RDWR); |
246 | 246 |
if (i) |
247 | 247 |
dup2(i, 0); |
248 | 248 |
dup2(i, 1); |
249 | 249 |
dup2(i, 2); |
250 |
+ if (i) |
|
251 |
+ close(i); |
|
250 | 252 |
} |
251 | 253 |
|
252 | 254 |
pstrcpy(pathname, sizeof(pathname), my_program_name); |
... | ... |
@@ -274,7 +313,6 @@ static int http_server(struct sockaddr_in my_addr) |
274 | 274 |
struct sockaddr_in from_addr; |
275 | 275 |
struct pollfd poll_table[HTTP_MAX_CONNECTIONS + 1], *poll_entry; |
276 | 276 |
HTTPContext *c, **cp; |
277 |
- long cur_time; |
|
278 | 277 |
|
279 | 278 |
server_fd = socket(AF_INET,SOCK_STREAM,0); |
280 | 279 |
if (server_fd < 0) { |
... | ... |
@@ -360,12 +398,17 @@ static int http_server(struct sockaddr_in my_addr) |
360 | 360 |
|
361 | 361 |
cur_time = gettime_ms(); |
362 | 362 |
|
363 |
+ if (need_to_start_children) { |
|
364 |
+ need_to_start_children = 0; |
|
365 |
+ start_children(first_feed); |
|
366 |
+ } |
|
367 |
+ |
|
363 | 368 |
/* now handle the events */ |
364 | 369 |
|
365 | 370 |
cp = &first_http_ctx; |
366 | 371 |
while ((*cp) != NULL) { |
367 | 372 |
c = *cp; |
368 |
- if (handle_http (c, cur_time) < 0) { |
|
373 |
+ if (handle_http (c) < 0) { |
|
369 | 374 |
/* close and free the connection */ |
370 | 375 |
log_connection(c); |
371 | 376 |
close(c->fd); |
... | ... |
@@ -430,7 +473,7 @@ static int http_server(struct sockaddr_in my_addr) |
430 | 430 |
} |
431 | 431 |
} |
432 | 432 |
|
433 |
-static int handle_http(HTTPContext *c, long cur_time) |
|
433 |
+static int handle_http(HTTPContext *c) |
|
434 | 434 |
{ |
435 | 435 |
int len; |
436 | 436 |
|
... | ... |
@@ -507,7 +550,7 @@ static int handle_http(HTTPContext *c, long cur_time) |
507 | 507 |
|
508 | 508 |
if (!(c->poll_entry->revents & POLLOUT)) |
509 | 509 |
return 0; |
510 |
- if (http_send_data(c, cur_time) < 0) |
|
510 |
+ if (http_send_data(c) < 0) |
|
511 | 511 |
return -1; |
512 | 512 |
break; |
513 | 513 |
case HTTPSTATE_RECEIVE_DATA: |
... | ... |
@@ -1207,7 +1250,7 @@ static void compute_stats(HTTPContext *c) |
1207 | 1207 |
|
1208 | 1208 |
#ifdef linux |
1209 | 1209 |
/* This is somewhat linux specific I guess */ |
1210 |
- snprintf(ps_cmd, sizeof(ps_cmd), "ps -o \"%%cpu,cputime\" --no-headers %d", stream->pid); |
|
1210 |
+ snprintf(ps_cmd, sizeof(ps_cmd), "ps -o \"%%cpu,bsdtime\" --no-headers %d", stream->pid); |
|
1211 | 1211 |
|
1212 | 1212 |
pid_stat = popen(ps_cmd, "r"); |
1213 | 1213 |
if (pid_stat) { |
... | ... |
@@ -1295,7 +1338,7 @@ static void compute_stats(HTTPContext *c) |
1295 | 1295 |
nb_bandwidth, nb_max_bandwidth); |
1296 | 1296 |
|
1297 | 1297 |
q += sprintf(q, "<TABLE>\n"); |
1298 |
- q += sprintf(q, "<TR><Th>#<Th>File<Th>IP<Th>State<Th>kbits/sec<Th>Size\n"); |
|
1298 |
+ q += sprintf(q, "<TR><Th>#<Th>File<Th>IP<Th>State<Th>Target bits/sec<Th>Actual bits/sec<Th>Bytes transferred\n"); |
|
1299 | 1299 |
c1 = first_http_ctx; |
1300 | 1300 |
i = 0; |
1301 | 1301 |
while (c1 != NULL && q < (char *) c->buffer + c->buffer_size - 2048) { |
... | ... |
@@ -1311,12 +1354,15 @@ static void compute_stats(HTTPContext *c) |
1311 | 1311 |
|
1312 | 1312 |
i++; |
1313 | 1313 |
p = inet_ntoa(c1->from_addr.sin_addr); |
1314 |
- q += sprintf(q, "<TR><TD><B>%d</B><TD>%s%s <TD> %s <TD> %s <td align=right> %d <TD align=right> ", |
|
1314 |
+ q += sprintf(q, "<TR><TD><B>%d</B><TD>%s%s <TD> %s <TD> %s <td align=right>", |
|
1315 | 1315 |
i, c1->stream->filename, |
1316 | 1316 |
c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "", |
1317 | 1317 |
p, |
1318 |
- http_state[c1->state], |
|
1319 |
- bitrate / 1000); |
|
1318 |
+ http_state[c1->state]); |
|
1319 |
+ q += fmt_bytecount(q, bitrate); |
|
1320 |
+ q += sprintf(q, "<td align=right>"); |
|
1321 |
+ q += fmt_bytecount(q, compute_datarate(&c1->datarate, c1->data_count) * 8); |
|
1322 |
+ q += sprintf(q, "<td align=right>"); |
|
1320 | 1323 |
q += fmt_bytecount(q, c1->data_count); |
1321 | 1324 |
*q++ = '\n'; |
1322 | 1325 |
c1 = c1->next; |
... | ... |
@@ -1414,7 +1460,7 @@ static int open_input_stream(HTTPContext *c, const char *info) |
1414 | 1414 |
return 0; |
1415 | 1415 |
} |
1416 | 1416 |
|
1417 |
-static int http_prepare_data(HTTPContext *c, long cur_time) |
|
1417 |
+static int http_prepare_data(HTTPContext *c) |
|
1418 | 1418 |
{ |
1419 | 1419 |
int i; |
1420 | 1420 |
|
... | ... |
@@ -1622,12 +1668,12 @@ static int http_prepare_data(HTTPContext *c, long cur_time) |
1622 | 1622 |
} |
1623 | 1623 |
|
1624 | 1624 |
/* should convert the format at the same time */ |
1625 |
-static int http_send_data(HTTPContext *c, long cur_time) |
|
1625 |
+static int http_send_data(HTTPContext *c) |
|
1626 | 1626 |
{ |
1627 | 1627 |
int len, ret; |
1628 | 1628 |
|
1629 | 1629 |
while (c->buffer_ptr >= c->buffer_end) { |
1630 |
- ret = http_prepare_data(c, cur_time); |
|
1630 |
+ ret = http_prepare_data(c); |
|
1631 | 1631 |
if (ret < 0) |
1632 | 1632 |
return -1; |
1633 | 1633 |
else if (ret == 0) { |
... | ... |
@@ -1648,6 +1694,7 @@ static int http_send_data(HTTPContext *c, long cur_time) |
1648 | 1648 |
} else { |
1649 | 1649 |
c->buffer_ptr += len; |
1650 | 1650 |
c->data_count += len; |
1651 |
+ update_datarate(&c->datarate, c->data_count); |
|
1651 | 1652 |
if (c->stream) |
1652 | 1653 |
c->stream->bytes_served += len; |
1653 | 1654 |
} |
... | ... |
@@ -1698,6 +1745,7 @@ static int http_receive_data(HTTPContext *c) |
1698 | 1698 |
} else { |
1699 | 1699 |
c->buffer_ptr += len; |
1700 | 1700 |
c->data_count += len; |
1701 |
+ update_datarate(&c->datarate, c->data_count); |
|
1701 | 1702 |
} |
1702 | 1703 |
} |
1703 | 1704 |
|
... | ... |
@@ -2505,10 +2553,37 @@ void licence(void) |
2505 | 2505 |
); |
2506 | 2506 |
} |
2507 | 2507 |
|
2508 |
+static void handle_child_exit(int sig) |
|
2509 |
+{ |
|
2510 |
+ pid_t pid; |
|
2511 |
+ int status; |
|
2512 |
+ |
|
2513 |
+ while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { |
|
2514 |
+ FFStream *feed; |
|
2515 |
+ |
|
2516 |
+ for (feed = first_feed; feed; feed = feed->next) { |
|
2517 |
+ if (feed->pid == pid) { |
|
2518 |
+ int uptime = time(0) - feed->pid_start; |
|
2519 |
+ |
|
2520 |
+ feed->pid = 0; |
|
2521 |
+ fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime); |
|
2522 |
+ |
|
2523 |
+ if (uptime < 30) { |
|
2524 |
+ /* Turn off any more restarts */ |
|
2525 |
+ feed->child_argv = 0; |
|
2526 |
+ } |
|
2527 |
+ } |
|
2528 |
+ } |
|
2529 |
+ } |
|
2530 |
+ |
|
2531 |
+ need_to_start_children = 1; |
|
2532 |
+} |
|
2533 |
+ |
|
2508 | 2534 |
int main(int argc, char **argv) |
2509 | 2535 |
{ |
2510 | 2536 |
const char *config_filename; |
2511 | 2537 |
int c; |
2538 |
+ struct sigaction sigact; |
|
2512 | 2539 |
|
2513 | 2540 |
register_all(); |
2514 | 2541 |
|
... | ... |
@@ -2553,6 +2628,11 @@ int main(int argc, char **argv) |
2553 | 2553 |
first_stream = NULL; |
2554 | 2554 |
logfilename[0] = '\0'; |
2555 | 2555 |
|
2556 |
+ memset(&sigact, 0, sizeof(sigact)); |
|
2557 |
+ sigact.sa_handler = handle_child_exit; |
|
2558 |
+ sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART; |
|
2559 |
+ sigaction(SIGCHLD, &sigact, 0); |
|
2560 |
+ |
|
2556 | 2561 |
if (parse_ffconfig(config_filename) < 0) { |
2557 | 2562 |
fprintf(stderr, "Incorrect config file - exiting.\n"); |
2558 | 2563 |
exit(1); |