Newer
Older
if (c->buffer_ptr >= c->buffer_end) {
/* all the buffer was sent : wait for a new request */
av_freep(&c->pb_buffer);
start_wait_request(c, 1);
}
Fabrice Bellard
committed
break;
case RTSPSTATE_SEND_PACKET:
if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
av_freep(&c->packet_buffer);
return -1;
}
/* no need to write if no events */
if (!(c->poll_entry->revents & POLLOUT))
return 0;
len = send(c->fd, c->packet_buffer_ptr,
c->packet_buffer_end - c->packet_buffer_ptr, 0);
if (ff_neterrno() != AVERROR(EAGAIN) &&
ff_neterrno() != AVERROR(EINTR)) {
/* error : close connection */
av_freep(&c->packet_buffer);
return -1;
}
c->packet_buffer_ptr += len;
if (c->packet_buffer_ptr >= c->packet_buffer_end) {
/* all the buffer was sent : wait for a new request */
av_freep(&c->packet_buffer);
c->state = RTSPSTATE_WAIT_REQUEST;
}
Fabrice Bellard
committed
case HTTPSTATE_READY:
/* nothing to do */
break;
close_connection:
av_freep(&c->pb_buffer);
return -1;
static int extract_rates(char *rates, int ratelen, const char *request)
{
const char *p;
for (p = request; *p && *p != '\r' && *p != '\n'; ) {
if (av_strncasecmp(p, "Pragma:", 7) == 0) {
const char *q = p + 7;
while (*q && *q != '\n' && av_isspace(*q))
q++;
if (av_strncasecmp(q, "stream-switch-entry=", 20) == 0) {
int stream_no;
int rate_no;
q += 20;
while (1) {
while (*q && *q != '\n' && *q != ':')
q++;
if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2)
break;
stream_no--;
if (stream_no < ratelen && stream_no >= 0)
rates[stream_no] = rate_no;
while (*q && *q != '\n' && !av_isspace(*q))
q++;
}
return 1;
}
}
p = strchr(p, '\n');
if (!p)
break;
p++;
}
return 0;
}
static int find_stream_in_feed(FFServerStream *feed, AVCodecContext *codec,
int bit_rate)
{
int i;
int best_bitrate = 100000000;
int best = -1;
for (i = 0; i < feed->nb_streams; i++) {
Michael Niedermayer
committed
AVCodecContext *feed_codec = feed->streams[i]->codec;
if (feed_codec->codec_id != codec->codec_id ||
feed_codec->sample_rate != codec->sample_rate ||
feed_codec->width != codec->width ||
feed_codec->height != codec->height)
continue;
/* Potential stream */
/* We want the fastest stream less than bit_rate, or the slowest
* faster than bit_rate
*/
if (feed_codec->bit_rate <= bit_rate) {
if (best_bitrate > bit_rate ||
feed_codec->bit_rate > best_bitrate) {
best_bitrate = feed_codec->bit_rate;
best = i;
}
continue;
}
if (feed_codec->bit_rate < best_bitrate) {
best_bitrate = feed_codec->bit_rate;
best = i;
}
}
return best;
}
static int modify_current_stream(HTTPContext *c, char *rates)
{
int i;
FFServerStream *req = c->stream;
/* Not much we can do for a feed */
if (!req->feed)
return 0;
for (i = 0; i < req->nb_streams; i++) {
Michael Niedermayer
committed
AVCodecContext *codec = req->streams[i]->codec;
switch(rates[i]) {
case 0:
c->switch_feed_streams[i] = req->feed_streams[i];
break;
case 1:
c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
break;
case 2:
/* Wants off or slow */
c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
#ifdef WANTS_OFF
/* This doesn't work well when it turns off the only stream! */
c->switch_feed_streams[i] = -2;
c->feed_streams[i] = -2;
#endif
break;
}
if (c->switch_feed_streams[i] >= 0 &&
c->switch_feed_streams[i] != c->feed_streams[i]) {
Fabrice Bellard
committed
static void get_word(char *buf, int buf_size, const char **pp)
{
const char *p;
char *q;
p = *pp;
p += strspn(p, SPACE_CHARS);
Fabrice Bellard
committed
q = buf;
while (!av_isspace(*p) && *p != '\0') {
Fabrice Bellard
committed
if ((q - buf) < buf_size - 1)
*q++ = *p;
p++;
}
if (buf_size > 0)
*q = '\0';
*pp = p;
}
static FFServerIPAddressACL* parse_dynamic_acl(FFServerStream *stream,
HTTPContext *c)
{
FILE* f;
char line[1024];
char cmd[1024];
FFServerIPAddressACL *acl = NULL;
int line_num = 0;
const char *p;
f = fopen(stream->dynamic_acl, "r");
if (!f) {
perror(stream->dynamic_acl);
return NULL;
}
acl = av_mallocz(sizeof(FFServerIPAddressACL));
if (!acl) {
fclose(f);
return NULL;
}
while (fgets(line, sizeof(line), f)) {
p++;
if (*p == '\0' || *p == '#')
continue;
ffserver_get_arg(cmd, sizeof(cmd), &p);
if (!av_strcasecmp(cmd, "ACL"))
ffserver_parse_acl_row(NULL, NULL, acl, p, stream->dynamic_acl,
line_num);
}
fclose(f);
return acl;
}
static void free_acl_list(FFServerIPAddressACL *in_acl)
FFServerIPAddressACL *pacl, *pacl2;
pacl = in_acl;
while(pacl) {
pacl2 = pacl;
pacl = pacl->next;
av_freep(pacl2);
}
}
static int validate_acl_list(FFServerIPAddressACL *in_acl, HTTPContext *c)
{
enum FFServerIPAddressAction last_action = IP_DENY;
FFServerIPAddressACL *acl;
struct in_addr *src = &c->from_addr.sin_addr;
unsigned long src_addr = src->s_addr;
for (acl = in_acl; acl; acl = acl->next) {
if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr)
return (acl->action == IP_ALLOW) ? 1 : 0;
last_action = acl->action;
}
/* Nothing matched, so return not the last action */
return (last_action == IP_DENY) ? 1 : 0;
}
static int validate_acl(FFServerStream *stream, HTTPContext *c)
FFServerIPAddressACL *acl;
/* if stream->acl is null validate_acl_list will return 1 */
ret = validate_acl_list(stream->acl, c);
if (stream->dynamic_acl[0]) {
acl = parse_dynamic_acl(stream, c);
ret = validate_acl_list(acl, c);
free_acl_list(acl);
}
return ret;
}
/**
* compute the real filename of a file by matching it without its
* extensions to all the stream's filenames
*/
Fabrice Bellard
committed
static void compute_real_filename(char *filename, int max_size)
{
char file1[1024];
char file2[1024];
char *p;
FFServerStream *stream;
Fabrice Bellard
committed
/* compute filename by matching without the file extensions */
av_strlcpy(file1, filename, sizeof(file1));
Fabrice Bellard
committed
p = strrchr(file1, '.');
if (p)
*p = '\0';
for(stream = config.first_stream; stream; stream = stream->next) {
av_strlcpy(file2, stream->filename, sizeof(file2));
Fabrice Bellard
committed
p = strrchr(file2, '.');
if (p)
*p = '\0';
if (!strcmp(file1, file2)) {
av_strlcpy(filename, stream->filename, max_size);
Fabrice Bellard
committed
break;
}
}
}
enum RedirType {
REDIR_NONE,
REDIR_ASX,
REDIR_RAM,
REDIR_ASF,
REDIR_RTSP,
REDIR_SDP,
};
/* parse HTTP request and prepare header */
Fabrice Bellard
committed
enum RedirType redir_type;
Alex Beregszaszi
committed
char info[1024], filename[1024];
char url[1024], *q;
char protocol[32];
char msg[1024];
const char *mime_type;
FFServerStream *stream;
char ratebuf[32];
get_word(cmd, sizeof(cmd), &p);
av_strlcpy(c->method, cmd, sizeof(c->method));
Alex Beregszaszi
committed
c->post = 0;
Alex Beregszaszi
committed
c->post = 1;
get_word(url, sizeof(url), &p);
av_strlcpy(c->url, url, sizeof(c->url));
Fabrice Bellard
committed
get_word(protocol, sizeof(protocol), (const char **)&p);
if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
return -1;
av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
http_log("%s - - New connection: %s %s\n",
inet_ntoa(c->from_addr.sin_addr), cmd, url);
/* find the filename and the optional info string in the request */
p1 = strchr(url, '?');
if (p1) {
av_strlcpy(info, p1, sizeof(info));
*p1 = '\0';
av_strlcpy(filename, url + ((*url == '/') ? 1 : 0), sizeof(filename)-1);
Alex Beregszaszi
committed
for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
if (av_strncasecmp(p, "User-Agent:", 11) == 0) {
if (*useragent && *useragent != '\n' && av_isspace(*useragent))
useragent++;
break;
}
p = strchr(p, '\n');
if (!p)
break;
p++;
}
Fabrice Bellard
committed
redir_type = REDIR_NONE;
if (av_match_ext(filename, "asx")) {
Fabrice Bellard
committed
redir_type = REDIR_ASX;
filename[strlen(filename)-1] = 'f';
} else if (av_match_ext(filename, "asf") &&
(!useragent || av_strncasecmp(useragent, "NSPlayer", 8))) {
/* if this isn't WMP or lookalike, return the redirector file */
Fabrice Bellard
committed
redir_type = REDIR_ASF;
} else if (av_match_ext(filename, "rpm,ram")) {
Fabrice Bellard
committed
redir_type = REDIR_RAM;
strcpy(filename + strlen(filename)-2, "m");
} else if (av_match_ext(filename, "rtsp")) {
Fabrice Bellard
committed
redir_type = REDIR_RTSP;
Alex Beregszaszi
committed
compute_real_filename(filename, sizeof(filename) - 1);
} else if (av_match_ext(filename, "sdp")) {
Fabrice Bellard
committed
redir_type = REDIR_SDP;
Alex Beregszaszi
committed
compute_real_filename(filename, sizeof(filename) - 1);
/* "redirect" request to index.html */
Alex Beregszaszi
committed
if (!strlen(filename))
av_strlcpy(filename, "index.html", sizeof(filename) - 1);
Alex Beregszaszi
committed
stream = config.first_stream;
if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
if (!stream) {
Philip Gladstone
committed
snprintf(msg, sizeof(msg), "File '%s' not found", url);
http_log("File '%s' not found\n", url);
c->stream = stream;
memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
if (stream->stream_type == STREAM_TYPE_REDIRECT) {
c->http_error = 301;
q = c->buffer;
snprintf(q, c->buffer_size,
"HTTP/1.0 301 Moved\r\n"
"Location: %s\r\n"
"Content-type: text/html\r\n"
"\r\n"
"<html><head><title>Moved</title></head><body>\r\n"
"You should be <a href=\"%s\">redirected</a>.\r\n"
"</body></html>\r\n",
stream->feed_filename, stream->feed_filename);
/* prepare output buffer */
c->buffer_ptr = c->buffer;
c->buffer_end = q;
c->state = HTTPSTATE_SEND_HEADER;
return 0;
}
/* If this is WMP, get the rate information */
if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
for (i = 0; i < FF_ARRAY_ELEMS(c->feed_streams); i++) {
}
if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE)
current_bandwidth += stream->bandwidth;
/* If already streaming this feed, do not let start another feeder. */
if (stream->feed_opened) {
snprintf(msg, sizeof(msg), "This feed is already being received.");
http_log("Feed '%s' already being received\n", stream->feed_filename);
goto send_error;
}
if (c->post == 0 && config.max_bandwidth < current_bandwidth) {
snprintf(q, c->buffer_size,
"HTTP/1.0 503 Server too busy\r\n"
"Content-type: text/html\r\n"
"\r\n"
"<html><head><title>Too busy</title></head><body>\r\n"
"<p>The server is too busy to serve your request at this time.</p>\r\n"
"<p>The bandwidth being served (including your stream) is %"PRIu64"kbit/sec, "
"and this exceeds the limit of %"PRIu64"kbit/sec.</p>\r\n"
"</body></html>\r\n",
current_bandwidth, config.max_bandwidth);
/* prepare output buffer */
c->buffer_ptr = c->buffer;
c->buffer_end = q;
c->state = HTTPSTATE_SEND_HEADER;
return 0;
}
Fabrice Bellard
committed
if (redir_type != REDIR_NONE) {
for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
if (av_strncasecmp(p, "Host:", 5) == 0) {
hostinfo = p + 5;
break;
}
p = strchr(p, '\n');
if (!p)
break;
p++;
}
if (hostinfo) {
char *eoh;
char hostbuf[260];
while (av_isspace(*hostinfo))
hostinfo++;
eoh = strchr(hostinfo, '\n');
if (eoh) {
if (eoh[-1] == '\r')
eoh--;
if (eoh - hostinfo < sizeof(hostbuf) - 1) {
memcpy(hostbuf, hostinfo, eoh - hostinfo);
hostbuf[eoh - hostinfo] = 0;
c->http_error = 200;
q = c->buffer;
Fabrice Bellard
committed
switch(redir_type) {
case REDIR_ASX:
snprintf(q, c->buffer_size,
"HTTP/1.0 200 ASX Follows\r\n"
"Content-type: video/x-ms-asf\r\n"
"\r\n"
"<ASX Version=\"3\">\r\n"
//"<!-- Autogenerated by ffserver -->\r\n"
"<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n"
"</ASX>\r\n", hostbuf, filename, info);
Fabrice Bellard
committed
break;
case REDIR_RAM:
snprintf(q, c->buffer_size,
"HTTP/1.0 200 RAM Follows\r\n"
"Content-type: audio/x-pn-realaudio\r\n"
"\r\n"
"# Autogenerated by ffserver\r\n"
"http://%s/%s%s\r\n", hostbuf, filename, info);
Fabrice Bellard
committed
break;
case REDIR_ASF:
snprintf(q, c->buffer_size,
"HTTP/1.0 200 ASF Redirect follows\r\n"
"Content-type: video/x-ms-asf\r\n"
"\r\n"
"[Reference]\r\n"
"Ref1=http://%s/%s%s\r\n", hostbuf, filename, info);
Fabrice Bellard
committed
break;
case REDIR_RTSP:
{
char hostname[256], *p;
/* extract only hostname */
av_strlcpy(hostname, hostbuf, sizeof(hostname));
Fabrice Bellard
committed
p = strrchr(hostname, ':');
if (p)
*p = '\0';
snprintf(q, c->buffer_size,
"HTTP/1.0 200 RTSP Redirect follows\r\n"
"Content-type: application/x-rtsp\r\n"
"\r\n"
"rtsp://%s:%d/%s\r\n", hostname, ntohs(config.rtsp_addr.sin_port), filename);
Fabrice Bellard
committed
}
break;
case REDIR_SDP:
{
int sdp_data_size;
socklen_t len;
Fabrice Bellard
committed
struct sockaddr_in my_addr;
snprintf(q, c->buffer_size,
"HTTP/1.0 200 OK\r\n"
"Content-type: application/sdp\r\n"
"\r\n");
Fabrice Bellard
committed
len = sizeof(my_addr);
/* XXX: Should probably fail? */
if (getsockname(c->fd, (struct sockaddr *)&my_addr, &len))
http_log("getsockname() failed\n");
Fabrice Bellard
committed
/* XXX: should use a dynamic buffer */
sdp_data_size = prepare_sdp_description(stream,
&sdp_data,
Fabrice Bellard
committed
my_addr.sin_addr);
if (sdp_data_size > 0) {
memcpy(q, sdp_data, sdp_data_size);
q += sdp_data_size;
*q = '\0';
av_free(sdp_data);
}
}
break;
default:
Fabrice Bellard
committed
break;
Fabrice Bellard
committed
}
/* prepare output buffer */
c->buffer_ptr = c->buffer;
c->buffer_end = q;
c->state = HTTPSTATE_SEND_HEADER;
return 0;
}
}
}
Philip Gladstone
committed
snprintf(msg, sizeof(msg), "ASX/RAM file not handled");
goto send_error;
stream->conns_served++;
Alex Beregszaszi
committed
if (c->post) {
/* if post, it means a feed is being sent */
if (!stream->is_feed) {
/* However it might be a status report from WMP! Let us log the
* data as it might come handy one day. */
int client_id = 0;
for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
if (av_strncasecmp(p, "Pragma: log-line=", 17) == 0) {
logline = p;
break;
}
if (av_strncasecmp(p, "Pragma: client-id=", 18) == 0)
client_id = strtol(p + 18, 0, 10);
p = strchr(p, '\n');
if (!p)
break;
p++;
}
if (logline) {
char *eol = strchr(logline, '\n');
logline += 17;
if (eol) {
if (eol[-1] == '\r')
eol--;
c->suppress_log = 1;
}
}
#ifdef DEBUG
http_log("\nGot request:\n%s\n", c->buffer);
#endif
if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
HTTPContext *wmpc;
/* Now we have to find the client_id */
for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
if (wmpc->wmp_client_id == client_id)
break;
}
if (wmpc && modify_current_stream(wmpc, ratebuf))
wmpc->switch_pending = 1;
}
Philip Gladstone
committed
snprintf(msg, sizeof(msg), "POST command not handled");
Philip Gladstone
committed
c->stream = 0;
goto send_error;
}
if (http_start_receive_data(c) < 0) {
Philip Gladstone
committed
snprintf(msg, sizeof(msg), "could not open feed");
goto send_error;
}
c->http_error = 0;
c->state = HTTPSTATE_RECEIVE_DATA;
return 0;
}
#ifdef DEBUG
if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0)
http_log("\nGot request:\n%s\n", c->buffer);
#endif
/* open input stream */
if (open_input_stream(c, info) < 0) {
Philip Gladstone
committed
snprintf(msg, sizeof(msg), "Input stream corresponding to '%s' not found", url);
/* prepare HTTP header */
c->buffer[0] = 0;
av_strlcatf(c->buffer, c->buffer_size, "HTTP/1.0 200 OK\r\n");
mime_type = "application/x-octet-stream";
av_strlcatf(c->buffer, c->buffer_size, "Pragma: no-cache\r\n");
if (!strcmp(c->stream->fmt->name,"asf_stream")) {
/* Need to allocate a client id */
c->wmp_client_id = av_lfg_get(&random_state);
av_strlcatf(c->buffer, c->buffer_size, "Server: Cougar 4.1.0.3923\r\nCache-Control: no-cache\r\nPragma: client-id=%d\r\nPragma: features=\"broadcast\"\r\n", c->wmp_client_id);
av_strlcatf(c->buffer, c->buffer_size, "Content-Type: %s\r\n", mime_type);
av_strlcatf(c->buffer, c->buffer_size, "\r\n");
q = c->buffer + strlen(c->buffer);
/* prepare output buffer */
c->http_error = 0;
c->buffer_ptr = c->buffer;
c->buffer_end = q;
c->state = HTTPSTATE_SEND_HEADER;
return 0;
send_error:
c->http_error = 404;
q = c->buffer;
Michael Niedermayer
committed
htmlstrip(msg);
snprintf(q, c->buffer_size,
"HTTP/1.0 404 Not Found\r\n"
"Content-type: text/html\r\n"
"\r\n"
"<html>\n"
"<head><title>404 Not Found</title></head>\n"
"<body>%s</body>\n"
"</html>\n", msg);
/* prepare output buffer */
c->buffer_ptr = c->buffer;
c->buffer_end = q;
c->state = HTTPSTATE_SEND_HEADER;
return 0;
send_status:
compute_status(c);
/* horrible: we use this value to avoid
* going to the send data state */
c->http_error = 200;
static void fmt_bytecount(AVIOContext *pb, int64_t count)
static const char suffix[] = " kMGTP";
const char *s;
for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++);
avio_printf(pb, "%"PRId64"%c", count, *s);
}
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
static inline void print_stream_params(AVIOContext *pb, FFServerStream *stream)
{
int i, stream_no;
const char *type = "unknown";
char parameters[64];
AVStream *st;
AVCodec *codec;
stream_no = stream->nb_streams;
avio_printf(pb, "<table cellspacing=0 cellpadding=4><tr><th>Stream<th>"
"type<th>kbits/s<th align=left>codec<th align=left>"
"Parameters\n");
for (i = 0; i < stream_no; i++) {
st = stream->streams[i];
codec = avcodec_find_encoder(st->codec->codec_id);
parameters[0] = 0;
switch(st->codec->codec_type) {
case AVMEDIA_TYPE_AUDIO:
type = "audio";
snprintf(parameters, sizeof(parameters), "%d channel(s), %d Hz",
st->codec->channels, st->codec->sample_rate);
break;
case AVMEDIA_TYPE_VIDEO:
type = "video";
snprintf(parameters, sizeof(parameters),
"%dx%d, q=%d-%d, fps=%d", st->codec->width,
st->codec->height, st->codec->qmin, st->codec->qmax,
st->codec->time_base.den / st->codec->time_base.num);
break;
default:
abort();
}
avio_printf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d"
"<td>%s<td>%s\n",
i, type, st->codec->bit_rate/1000,
codec ? codec->name : "", parameters);
}
avio_printf(pb, "</table>\n");
}
static void compute_status(HTTPContext *c)
FFServerStream *stream;
Fabrice Bellard
committed
char *p;
Fabrice Bellard
committed
int i, len;
if (avio_open_dyn_buf(&pb) < 0) {
Fabrice Bellard
committed
/* XXX: return an error ? */
Fabrice Bellard
committed
c->buffer_end = c->buffer;
return;
avio_printf(pb, "HTTP/1.0 200 OK\r\n");
avio_printf(pb, "Content-type: text/html\r\n");
avio_printf(pb, "Pragma: no-cache\r\n");
avio_printf(pb, "\r\n");
avio_printf(pb, "<html><head><title>%s Status</title>\n", program_name);
Erik Hovland
committed
if (c->stream->feed_filename[0])
avio_printf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n",
c->stream->feed_filename);
avio_printf(pb, "</head>\n<body>");
avio_printf(pb, "<h1>%s Status</h1>\n", program_name);
avio_printf(pb, "<h2>Available Streams</h2>\n");
avio_printf(pb, "<table cellspacing=0 cellpadding=4>\n");
avio_printf(pb, "<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 = config.first_stream;
char sfilename[1024];
char *eosf;
if (stream->feed == stream) {
stream = stream->next;
continue;
}
av_strlcpy(sfilename, stream->filename, sizeof(sfilename) - 10);
eosf = sfilename + strlen(sfilename);
if (eosf - sfilename >= 4) {
if (strcmp(eosf - 4, ".asf") == 0)
strcpy(eosf - 4, ".asx");
else if (strcmp(eosf - 3, ".rm") == 0)
strcpy(eosf - 3, ".ram");
else if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
/* generate a sample RTSP director if
* unicast. Generate an SDP redirector if
* multicast */
eosf = strrchr(sfilename, '.');
if (!eosf)
eosf = sfilename + strlen(sfilename);
if (stream->is_multicast)
strcpy(eosf, ".sdp");
else
strcpy(eosf, ".rtsp");
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
avio_printf(pb, "<tr><td><a href=\"/%s\">%s</a> ",
sfilename, stream->filename);
avio_printf(pb, "<td align=right> %d <td align=right> ",
stream->conns_served);
fmt_bytecount(pb, stream->bytes_served);
switch(stream->stream_type) {
case STREAM_TYPE_LIVE: {
int audio_bit_rate = 0;
int video_bit_rate = 0;
const char *audio_codec_name = "";
const char *video_codec_name = "";
const char *audio_codec_name_extra = "";
const char *video_codec_name_extra = "";
for(i=0;i<stream->nb_streams;i++) {
AVStream *st = stream->streams[i];
AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
switch(st->codec->codec_type) {
case AVMEDIA_TYPE_AUDIO:
audio_bit_rate += st->codec->bit_rate;
if (codec) {
if (*audio_codec_name)
audio_codec_name_extra = "...";
audio_codec_name = codec->name;
break;
case AVMEDIA_TYPE_VIDEO:
video_bit_rate += st->codec->bit_rate;
if (codec) {
if (*video_codec_name)
video_codec_name_extra = "...";
video_codec_name = codec->name;
}
break;
case AVMEDIA_TYPE_DATA:
video_bit_rate += st->codec->bit_rate;
break;
default:
abort();
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
avio_printf(pb, "<td align=center> %s <td align=right> %d "
"<td align=right> %d <td> %s %s <td align=right> "
"%d <td> %s %s",
stream->fmt->name, stream->bandwidth,
video_bit_rate / 1000, video_codec_name,
video_codec_name_extra, audio_bit_rate / 1000,
audio_codec_name, audio_codec_name_extra);
if (stream->feed)
avio_printf(pb, "<td>%s", stream->feed->filename);
else
avio_printf(pb, "<td>%s", stream->feed_filename);
avio_printf(pb, "\n");
}
break;
default:
avio_printf(pb, "<td align=center> - <td align=right> - "
"<td align=right> - <td><td align=right> - <td>\n");
break;
}
stream = config.first_stream;
if (stream->feed != stream) {
stream = stream->next;
continue;
}
avio_printf(pb, "<h2>Feed %s</h2>", stream->filename);
if (stream->pid) {
avio_printf(pb, "Running as pid %"PRId64".\n", (int64_t) stream->pid);
{
FILE *pid_stat;
char ps_cmd[64];
/* This is somewhat linux specific I guess */
snprintf(ps_cmd, sizeof(ps_cmd),
"ps -o \"%%cpu,cputime\" --no-headers %"PRId64"",
(int64_t) stream->pid);
pid_stat = popen(ps_cmd, "r");
if (pid_stat) {
char cpuperc[10];
char cpuused[64];
if (fscanf(pid_stat, "%9s %63s", cpuperc, cpuused) == 2) {
avio_printf(pb, "Currently using %s%% of the cpu. "
"Total time used %s.\n",
cpuperc, cpuused);
}
fclose(pid_stat);
}
}
avio_printf(pb, "<p>");
}
print_stream_params(pb, stream);
stream = stream->next;
}
avio_printf(pb, "<h2>Connection Status</h2>\n");
avio_printf(pb, "Number of connections: %d / %d<br>\n",
nb_connections, config.nb_max_connections);
avio_printf(pb, "Bandwidth in use: %"PRIu64"k / %"PRIu64"k<br>\n",
current_bandwidth, config.max_bandwidth);
avio_printf(pb, "<tr><th>#<th>File<th>IP<th>Proto<th>State<th>Target "
"bits/sec<th>Actual bits/sec<th>Bytes transferred\n");
int bitrate;
int j;
bitrate = 0;
Fabrice Bellard
committed
if (c1->stream) {
for (j = 0; j < c1->stream->nb_streams; j++) {
Michael Niedermayer
committed
bitrate += c1->stream->streams[j]->codec->bit_rate;
else if (c1->feed_streams[j] >= 0)
bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec->bit_rate;