Newer
Older
if (ff_neterrno() != AVERROR(EAGAIN) &&
ff_neterrno() != AVERROR(EINTR))
/* search for end of request. */
c->buffer_ptr += len;
ptr = c->buffer_ptr;
if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
(ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
/* request found : parse it and reply */
if (c->state == HTTPSTATE_WAIT_REQUEST)
ret = http_parse_request(c);
else
ret = rtsp_parse_request(c);
} else if (ptr >= c->buffer_end) {
/* request too long: cannot do anything */
return -1;
} else goto read_loop;
break;
case HTTPSTATE_SEND_HEADER:
if (c->poll_entry->revents & (POLLERR | POLLHUP))
return -1;
Fabrice Bellard
committed
/* no need to write if no events */
len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
if (ff_neterrno() != AVERROR(EAGAIN) &&
ff_neterrno() != AVERROR(EINTR)) {
goto close_connection;
c->buffer_ptr += len;
if (c->stream)
c->stream->bytes_served += len;
c->data_count += len;
if (c->buffer_ptr >= c->buffer_end) {
av_freep(&c->pb_buffer);
/* if error, exit */
if (c->http_error)
return -1;
/* all the buffer was sent : synchronize to the incoming
* stream */
c->state = HTTPSTATE_SEND_DATA_HEADER;
c->buffer_ptr = c->buffer_end = c->buffer;
}
break;
case HTTPSTATE_SEND_DATA:
case HTTPSTATE_SEND_DATA_HEADER:
case HTTPSTATE_SEND_DATA_TRAILER:
Fabrice Bellard
committed
/* for packetized output, we consider we can always write (the
* input streams set the speed). It may be better to verify
* that we do not rely too much on the kernel queues */
Fabrice Bellard
committed
if (!c->is_packetized) {
if (c->poll_entry->revents & (POLLERR | POLLHUP))
return -1;
Fabrice Bellard
committed
/* no need to read if no events */
if (!(c->poll_entry->revents & POLLOUT))
return 0;
}
if (http_send_data(c) < 0)
/* close connection if trailer sent */
if (c->state == HTTPSTATE_SEND_DATA_TRAILER)
return -1;
/* Check if it is a single jpeg frame 123 */
if (c->stream->single_frame && c->data_count > c->cur_frame_bytes && c->cur_frame_bytes > 0) {
close_connection(c);
}
break;
case HTTPSTATE_RECEIVE_DATA:
/* no need to read if no events */
if (c->poll_entry->revents & (POLLERR | POLLHUP))
return -1;
if (!(c->poll_entry->revents & POLLIN))
return 0;
if (http_receive_data(c) < 0)
return -1;
break;
case HTTPSTATE_WAIT_FEED:
/* no need to read if no events */
if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
return -1;
/* nothing to do, we'll be waken up by incoming feed packets */
break;
Fabrice Bellard
committed
case RTSPSTATE_SEND_REPLY:
if (c->poll_entry->revents & (POLLERR | POLLHUP))
goto close_connection;
Fabrice Bellard
committed
/* no need to write if no events */
if (!(c->poll_entry->revents & POLLOUT))
return 0;
len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
Fabrice Bellard
committed
if (len < 0) {
if (ff_neterrno() != AVERROR(EAGAIN) &&
ff_neterrno() != AVERROR(EINTR)) {
goto close_connection;
Fabrice Bellard
committed
}
c->buffer_ptr += len;
c->data_count += len;
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, AVCodecParameters *codec,
{
int i;
int best_bitrate = 100000000;
int best = -1;
for (i = 0; i < feed->nb_streams; i++) {
AVCodecParameters *feed_codec = feed->streams[i]->codecpar;
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++) {
AVCodecParameters *codec = req->streams[i]->codecpar;
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;
Michael Niedermayer
committed
#define SPACE_CHARS " \t\r\n"
Fabrice Bellard
committed
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
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];
Reynaldo H. Verdejo Pinochet
committed
char *encoded_msg = NULL;
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"
"<!DOCTYPE html>\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 another feeder start */
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"
"<!DOCTYPE html>\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/s, and this exceeds the limit of "
"%"PRIu64"kbit/s.</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';
Michael Niedermayer
committed
av_freep(&sdp_data);
Fabrice Bellard
committed
}
}
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;
Reynaldo H. Verdejo Pinochet
committed
if (!htmlencode(msg, &encoded_msg)) {
http_log("Could not encode filename '%s' as HTML\n", msg);
}
snprintf(q, c->buffer_size,
"HTTP/1.0 404 Not Found\r\n"
"Content-type: text/html\r\n"
"\r\n"
"<!DOCTYPE html>\n"
"<head>\n"
Reynaldo H. Verdejo Pinochet
committed
"<meta charset=\"UTF-8\">\n"
"<title>404 Not Found</title>\n"
"</head>\n"
Reynaldo H. Verdejo Pinochet
committed
"</html>\n", encoded_msg? encoded_msg : "File not found");
/* prepare output buffer */
c->buffer_ptr = c->buffer;
c->buffer_end = q;
c->state = HTTPSTATE_SEND_HEADER;
Reynaldo H. Verdejo Pinochet
committed
av_freep(&encoded_msg);
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);
}
static inline void print_stream_params(AVIOContext *pb, FFServerStream *stream)
{
int i, stream_no;
const char *type = "unknown";
char parameters[64];
Michael Niedermayer
committed
LayeredAVStream *st;
AVCodec *codec;
stream_no = stream->nb_streams;
avio_printf(pb, "<table><tr><th>Stream<th>"
"type<th>kbit/s<th>codec<th>"
"Parameters\n");
for (i = 0; i < stream_no; i++) {
st = stream->streams[i];
Reynaldo H. Verdejo Pinochet
committed
codec = avcodec_find_encoder(st->codecpar->codec_id);
parameters[0] = 0;
Reynaldo H. Verdejo Pinochet
committed
switch(st->codecpar->codec_type) {
case AVMEDIA_TYPE_AUDIO:
type = "audio";
snprintf(parameters, sizeof(parameters), "%d channel(s), %d Hz",
Reynaldo H. Verdejo Pinochet
committed
st->codecpar->channels, st->codecpar->sample_rate);
break;
case AVMEDIA_TYPE_VIDEO:
type = "video";
snprintf(parameters, sizeof(parameters),
Reynaldo H. Verdejo Pinochet
committed
"%dx%d, q=%d-%d, fps=%d", st->codecpar->width,
st->codecpar->height, st->codec->qmin, st->codec->qmax,
Reynaldo H. Verdejo Pinochet
committed
st->time_base.den / st->time_base.num);
break;
default:
abort();
}
avio_printf(pb, "<tr><td>%d<td>%s<td>%"PRId64
"<td>%s<td>%s\n",
Reynaldo H. Verdejo Pinochet
committed
i, type, (int64_t)st->codecpar->bit_rate/1000,
codec ? codec->name : "", parameters);
}
avio_printf(pb, "</table>\n");
}
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
static void clean_html(char *clean, int clean_len, char *dirty)
{
int i, o;
for (o = i = 0; o+10 < clean_len && dirty[i];) {
int len = strspn(dirty+i, "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$-_.+!*(),?/ :;%");
if (len) {
if (o + len >= clean_len)
break;
memcpy(clean + o, dirty + i, len);
i += len;
o += len;
} else {
int c = dirty[i++];
switch (c) {
case '&': av_strlcat(clean+o, "&" , clean_len - o); break;
case '<': av_strlcat(clean+o, "<" , clean_len - o); break;
case '>': av_strlcat(clean+o, ">" , clean_len - o); break;
case '\'': av_strlcat(clean+o, "'" , clean_len - o); break;
case '\"': av_strlcat(clean+o, """ , clean_len - o); break;
default: av_strlcat(clean+o, "☹", clean_len - o); break;
}
o += strlen(clean+o);
}
}
clean[o] = 0;
}
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, "<!DOCTYPE html>\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>\n");
avio_printf(pb, "<tr><th>Path<th>Served<br>Conns<th><br>bytes<th>Format<th>Bit rate<br>kbit/s<th>Video<br>kbit/s<th><br>Codec<th>Audio<br>kbit/s<th><br>Codec<th>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");