mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2025-02-12 04:44:25 +00:00
* Give the max stream time configuration item a chance of working
* Add parameters to set author/comment/copyright/title to streamed asf * Format the stats output a little bit better. Originally committed as revision 663 to svn://svn.ffmpeg.org/ffmpeg/trunk
This commit is contained in:
parent
2824c47368
commit
2ac887bae9
85
ffserver.c
85
ffserver.c
@ -95,7 +95,7 @@ typedef struct HTTPContext {
|
|||||||
int last_packet_sent; /* true if last data packet was sent */
|
int last_packet_sent; /* true if last data packet was sent */
|
||||||
int suppress_log;
|
int suppress_log;
|
||||||
int bandwidth;
|
int bandwidth;
|
||||||
time_t start_time;
|
long start_time; /* In milliseconds - this wraps fairly often */
|
||||||
int wmp_client_id;
|
int wmp_client_id;
|
||||||
char protocol[16];
|
char protocol[16];
|
||||||
char method[16];
|
char method[16];
|
||||||
@ -121,12 +121,16 @@ typedef struct FFStream {
|
|||||||
AVOutputFormat *fmt;
|
AVOutputFormat *fmt;
|
||||||
int nb_streams;
|
int nb_streams;
|
||||||
int prebuffer; /* Number of millseconds early to start */
|
int prebuffer; /* Number of millseconds early to start */
|
||||||
time_t max_time;
|
long max_time; /* Number of milliseconds to run */
|
||||||
int send_on_key;
|
int send_on_key;
|
||||||
AVStream *streams[MAX_STREAMS];
|
AVStream *streams[MAX_STREAMS];
|
||||||
int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
|
int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
|
||||||
char feed_filename[1024]; /* file name of the feed storage, or
|
char feed_filename[1024]; /* file name of the feed storage, or
|
||||||
input file name for a stream */
|
input file name for a stream */
|
||||||
|
char author[512];
|
||||||
|
char title[512];
|
||||||
|
char copyright[512];
|
||||||
|
char comment[512];
|
||||||
pid_t pid; /* Of ffmpeg process */
|
pid_t pid; /* Of ffmpeg process */
|
||||||
char **child_argv;
|
char **child_argv;
|
||||||
struct FFStream *next;
|
struct FFStream *next;
|
||||||
@ -154,7 +158,7 @@ FFStream *first_stream; /* contains all streams, including feeds */
|
|||||||
|
|
||||||
static int handle_http(HTTPContext *c, long cur_time);
|
static int handle_http(HTTPContext *c, long cur_time);
|
||||||
static int http_parse_request(HTTPContext *c);
|
static int http_parse_request(HTTPContext *c);
|
||||||
static int http_send_data(HTTPContext *c);
|
static int http_send_data(HTTPContext *c, long cur_time);
|
||||||
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);
|
||||||
@ -162,6 +166,9 @@ static int http_receive_data(HTTPContext *c);
|
|||||||
|
|
||||||
static const char *my_program_name;
|
static const char *my_program_name;
|
||||||
|
|
||||||
|
static int ffserver_debug;
|
||||||
|
static int no_launch;
|
||||||
|
|
||||||
int nb_max_connections;
|
int nb_max_connections;
|
||||||
int nb_connections;
|
int nb_connections;
|
||||||
|
|
||||||
@ -213,6 +220,9 @@ static void log_connection(HTTPContext *c)
|
|||||||
|
|
||||||
static void start_children(FFStream *feed)
|
static void start_children(FFStream *feed)
|
||||||
{
|
{
|
||||||
|
if (no_launch)
|
||||||
|
return;
|
||||||
|
|
||||||
for (; feed; feed = feed->next) {
|
for (; feed; feed = feed->next) {
|
||||||
if (feed->child_argv) {
|
if (feed->child_argv) {
|
||||||
feed->pid = fork();
|
feed->pid = fork();
|
||||||
@ -227,6 +237,7 @@ static void start_children(FFStream *feed)
|
|||||||
char *slash;
|
char *slash;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
if (!ffserver_debug) {
|
||||||
for (i = 0; i < 10; i++) {
|
for (i = 0; i < 10; i++) {
|
||||||
close(i);
|
close(i);
|
||||||
}
|
}
|
||||||
@ -236,6 +247,7 @@ static void start_children(FFStream *feed)
|
|||||||
dup2(i, 0);
|
dup2(i, 0);
|
||||||
dup2(i, 1);
|
dup2(i, 1);
|
||||||
dup2(i, 2);
|
dup2(i, 2);
|
||||||
|
}
|
||||||
|
|
||||||
pstrcpy(pathname, sizeof(pathname), my_program_name);
|
pstrcpy(pathname, sizeof(pathname), my_program_name);
|
||||||
|
|
||||||
@ -404,6 +416,7 @@ static int http_server(struct sockaddr_in my_addr)
|
|||||||
c->buffer_ptr = c->buffer;
|
c->buffer_ptr = c->buffer;
|
||||||
c->buffer_end = c->buffer + c->buffer_size;
|
c->buffer_end = c->buffer + c->buffer_size;
|
||||||
c->timeout = cur_time + REQUEST_TIMEOUT;
|
c->timeout = cur_time + REQUEST_TIMEOUT;
|
||||||
|
c->start_time = cur_time;
|
||||||
nb_connections++;
|
nb_connections++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -494,7 +507,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) < 0)
|
if (http_send_data(c, cur_time) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
break;
|
break;
|
||||||
case HTTPSTATE_RECEIVE_DATA:
|
case HTTPSTATE_RECEIVE_DATA:
|
||||||
@ -1060,6 +1073,17 @@ static int http_parse_request(HTTPContext *c)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int fmt_bytecount(char *q, INT64 count)
|
||||||
|
{
|
||||||
|
static const char *suffix = " kMGTP";
|
||||||
|
const char *s;
|
||||||
|
|
||||||
|
for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++) {
|
||||||
|
}
|
||||||
|
|
||||||
|
return sprintf(q, "%lld%c", count, *s);
|
||||||
|
}
|
||||||
|
|
||||||
static void compute_stats(HTTPContext *c)
|
static void compute_stats(HTTPContext *c)
|
||||||
{
|
{
|
||||||
HTTPContext *c1;
|
HTTPContext *c1;
|
||||||
@ -1093,7 +1117,7 @@ static void compute_stats(HTTPContext *c)
|
|||||||
/* format status */
|
/* format status */
|
||||||
q += sprintf(q, "<H2>Available Streams</H2>\n");
|
q += sprintf(q, "<H2>Available Streams</H2>\n");
|
||||||
q += sprintf(q, "<TABLE cellspacing=0 cellpadding=4>\n");
|
q += sprintf(q, "<TABLE cellspacing=0 cellpadding=4>\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");
|
q += sprintf(q, "<TR><Th valign=top>Path<th align=left>Served<br>Conns<Th><br>bytes<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];
|
||||||
@ -1112,8 +1136,9 @@ 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",
|
q += sprintf(q, "<td align=right> %d <td align=right> ",
|
||||||
stream->conns_served, stream->bytes_served / 1000);
|
stream->conns_served);
|
||||||
|
q += fmt_bytecount(q, stream->bytes_served);
|
||||||
switch(stream->stream_type) {
|
switch(stream->stream_type) {
|
||||||
case STREAM_TYPE_LIVE:
|
case STREAM_TYPE_LIVE:
|
||||||
{
|
{
|
||||||
@ -1286,13 +1311,14 @@ 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> %Ld\n",
|
q += sprintf(q, "<TR><TD><B>%d</B><TD>%s%s <TD> %s <TD> %s <td align=right> %d <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,
|
bitrate / 1000);
|
||||||
c1->data_count);
|
q += fmt_bytecount(q, c1->data_count);
|
||||||
|
*q++ = '\n';
|
||||||
c1 = c1->next;
|
c1 = c1->next;
|
||||||
}
|
}
|
||||||
q += sprintf(q, "</TABLE>\n");
|
q += sprintf(q, "</TABLE>\n");
|
||||||
@ -1388,13 +1414,18 @@ static int open_input_stream(HTTPContext *c, const char *info)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int http_prepare_data(HTTPContext *c)
|
static int http_prepare_data(HTTPContext *c, long cur_time)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
switch(c->state) {
|
switch(c->state) {
|
||||||
case HTTPSTATE_SEND_DATA_HEADER:
|
case HTTPSTATE_SEND_DATA_HEADER:
|
||||||
memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
|
memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
|
||||||
|
pstrcpy(c->fmt_ctx.author, sizeof(c->fmt_ctx.author), c->stream->author);
|
||||||
|
pstrcpy(c->fmt_ctx.comment, sizeof(c->fmt_ctx.comment), c->stream->comment);
|
||||||
|
pstrcpy(c->fmt_ctx.copyright, sizeof(c->fmt_ctx.copyright), c->stream->copyright);
|
||||||
|
pstrcpy(c->fmt_ctx.title, sizeof(c->fmt_ctx.title), c->stream->title);
|
||||||
|
|
||||||
if (c->stream->feed) {
|
if (c->stream->feed) {
|
||||||
/* open output stream by using specified codecs */
|
/* open output stream by using specified codecs */
|
||||||
c->fmt_ctx.oformat = c->stream->fmt;
|
c->fmt_ctx.oformat = c->stream->fmt;
|
||||||
@ -1504,7 +1535,7 @@ static int http_prepare_data(HTTPContext *c)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (c->stream->max_time &&
|
if (c->stream->max_time &&
|
||||||
c->stream->max_time + c->start_time > time(0)) {
|
c->stream->max_time + c->start_time - cur_time < 0) {
|
||||||
/* We have timed out */
|
/* We have timed out */
|
||||||
c->state = HTTPSTATE_SEND_DATA_TRAILER;
|
c->state = HTTPSTATE_SEND_DATA_TRAILER;
|
||||||
} else if (av_read_packet(c->fmt_in, &pkt) < 0) {
|
} else if (av_read_packet(c->fmt_in, &pkt) < 0) {
|
||||||
@ -1591,12 +1622,12 @@ static int http_prepare_data(HTTPContext *c)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* should convert the format at the same time */
|
/* should convert the format at the same time */
|
||||||
static int http_send_data(HTTPContext *c)
|
static int http_send_data(HTTPContext *c, long cur_time)
|
||||||
{
|
{
|
||||||
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);
|
ret = http_prepare_data(c, cur_time);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return -1;
|
return -1;
|
||||||
else if (ret == 0) {
|
else if (ret == 0) {
|
||||||
@ -2223,6 +2254,22 @@ int parse_ffconfig(const char *filename)
|
|||||||
filename, line_num);
|
filename, line_num);
|
||||||
errors++;
|
errors++;
|
||||||
}
|
}
|
||||||
|
} else if (!strcasecmp(cmd, "Author")) {
|
||||||
|
if (stream) {
|
||||||
|
get_arg(stream->author, sizeof(stream->author), &p);
|
||||||
|
}
|
||||||
|
} else if (!strcasecmp(cmd, "Comment")) {
|
||||||
|
if (stream) {
|
||||||
|
get_arg(stream->comment, sizeof(stream->comment), &p);
|
||||||
|
}
|
||||||
|
} else if (!strcasecmp(cmd, "Copyright")) {
|
||||||
|
if (stream) {
|
||||||
|
get_arg(stream->copyright, sizeof(stream->copyright), &p);
|
||||||
|
}
|
||||||
|
} else if (!strcasecmp(cmd, "Title")) {
|
||||||
|
if (stream) {
|
||||||
|
get_arg(stream->title, sizeof(stream->title), &p);
|
||||||
|
}
|
||||||
} else if (!strcasecmp(cmd, "Preroll")) {
|
} else if (!strcasecmp(cmd, "Preroll")) {
|
||||||
get_arg(arg, sizeof(arg), &p);
|
get_arg(arg, sizeof(arg), &p);
|
||||||
if (stream) {
|
if (stream) {
|
||||||
@ -2251,7 +2298,7 @@ int parse_ffconfig(const char *filename)
|
|||||||
} else if (!strcasecmp(cmd, "MaxTime")) {
|
} else if (!strcasecmp(cmd, "MaxTime")) {
|
||||||
get_arg(arg, sizeof(arg), &p);
|
get_arg(arg, sizeof(arg), &p);
|
||||||
if (stream) {
|
if (stream) {
|
||||||
stream->max_time = atoi(arg);
|
stream->max_time = atoi(arg) * 1000;
|
||||||
}
|
}
|
||||||
} else if (!strcasecmp(cmd, "AudioBitRate")) {
|
} else if (!strcasecmp(cmd, "AudioBitRate")) {
|
||||||
get_arg(arg, sizeof(arg), &p);
|
get_arg(arg, sizeof(arg), &p);
|
||||||
@ -2470,7 +2517,7 @@ int main(int argc, char **argv)
|
|||||||
my_program_name = argv[0];
|
my_program_name = argv[0];
|
||||||
|
|
||||||
for(;;) {
|
for(;;) {
|
||||||
c = getopt_long_only(argc, argv, "Lh?f:", NULL, NULL);
|
c = getopt_long_only(argc, argv, "ndLh?f:", NULL, NULL);
|
||||||
if (c == -1)
|
if (c == -1)
|
||||||
break;
|
break;
|
||||||
switch(c) {
|
switch(c) {
|
||||||
@ -2481,6 +2528,12 @@ int main(int argc, char **argv)
|
|||||||
case 'h':
|
case 'h':
|
||||||
help();
|
help();
|
||||||
exit(1);
|
exit(1);
|
||||||
|
case 'n':
|
||||||
|
no_launch = 1;
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
ffserver_debug = 1;
|
||||||
|
break;
|
||||||
case 'f':
|
case 'f':
|
||||||
config_filename = optarg;
|
config_filename = optarg;
|
||||||
break;
|
break;
|
||||||
|
Loading…
Reference in New Issue
Block a user