Newer
Older
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)) {
av_freep(&c->pb_buffer);
return -1;
}
/* 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)) {
Fabrice Bellard
committed
/* error : close connection */
av_freep(&c->pb_buffer);
return -1;
}
} else {
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);
}
}
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;
}
} else {
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;
}
}
break;
Fabrice Bellard
committed
case HTTPSTATE_READY:
/* nothing to do */
break;
static int extract_rates(char *rates, int ratelen, const char *request)
{
const char *p;
for (p = request; *p && *p != '\r' && *p != '\n'; ) {
if (strncasecmp(p, "Pragma:", 7) == 0) {
const char *q = p + 7;
while (*q && *q != '\n' && isspace(*q))
q++;
if (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' && !isspace(*q))
q++;
}
return 1;
}
}
p = strchr(p, '\n');
if (!p)
break;
p++;
}
return 0;
}
static int find_stream_in_feed(FFStream *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
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
* 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;
}
} else {
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;
FFStream *req = c->stream;
int action_required = 0;
/* 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])
action_required = 1;
}
Fabrice Bellard
committed
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
/* XXX: factorize in utils.c ? */
/* XXX: take care with different space meaning */
static void skip_spaces(const char **pp)
{
const char *p;
p = *pp;
while (*p == ' ' || *p == '\t')
p++;
*pp = p;
}
static void get_word(char *buf, int buf_size, const char **pp)
{
const char *p;
char *q;
p = *pp;
skip_spaces(&p);
q = buf;
while (!isspace(*p) && *p != '\0') {
if ((q - buf) < buf_size - 1)
*q++ = *p;
p++;
}
if (buf_size > 0)
*q = '\0';
*pp = p;
}
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
static void get_arg(char *buf, int buf_size, const char **pp)
{
const char *p;
char *q;
int quote;
p = *pp;
while (isspace(*p)) p++;
q = buf;
quote = 0;
if (*p == '\"' || *p == '\'')
quote = *p++;
for(;;) {
if (quote) {
if (*p == quote)
break;
} else {
if (isspace(*p))
break;
}
if (*p == '\0')
break;
if ((q - buf) < buf_size - 1)
*q++ = *p;
p++;
}
*q = '\0';
if (quote && *p == quote)
p++;
*pp = p;
}
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
static void parse_acl_row(FFStream *stream, FFStream* feed, IPAddressACL *ext_acl,
const char *p, const char *filename, int line_num)
{
char arg[1024];
IPAddressACL acl;
int errors = 0;
get_arg(arg, sizeof(arg), &p);
if (strcasecmp(arg, "allow") == 0)
acl.action = IP_ALLOW;
else if (strcasecmp(arg, "deny") == 0)
acl.action = IP_DENY;
else {
fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
filename, line_num, arg);
errors++;
}
get_arg(arg, sizeof(arg), &p);
if (resolve_host(&acl.first, arg) != 0) {
fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
filename, line_num, arg);
errors++;
} else
acl.last = acl.first;
get_arg(arg, sizeof(arg), &p);
if (arg[0]) {
if (resolve_host(&acl.last, arg) != 0) {
fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
filename, line_num, arg);
errors++;
}
}
if (!errors) {
IPAddressACL *nacl = av_mallocz(sizeof(*nacl));
IPAddressACL **naclp = 0;
acl.next = 0;
*nacl = acl;
if (stream)
naclp = &stream->acl;
else if (feed)
naclp = &feed->acl;
else if (ext_acl)
naclp = &ext_acl;
else {
fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
filename, line_num);
errors++;
}
if (naclp) {
while (*naclp)
naclp = &(*naclp)->next;
*naclp = nacl;
}
}
}
static IPAddressACL* parse_dynamic_acl(FFStream *stream, HTTPContext *c)
{
FILE* f;
char line[1024];
char cmd[1024];
IPAddressACL *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(IPAddressACL));
/* Build ACL */
for(;;) {
if (fgets(line, sizeof(line), f) == NULL)
break;
line_num++;
p = line;
while (isspace(*p))
p++;
if (*p == '\0' || *p == '#')
continue;
get_arg(cmd, sizeof(cmd), &p);
if (!strcasecmp(cmd, "ACL"))
parse_acl_row(NULL, NULL, acl, p, stream->dynamic_acl, line_num);
}
fclose(f);
return acl;
}
static void free_acl_list(IPAddressACL *in_acl)
{
IPAddressACL *pacl,*pacl2;
pacl = in_acl;
while(pacl) {
pacl2 = pacl;
pacl = pacl->next;
av_freep(pacl2);
}
}
static int validate_acl_list(IPAddressACL *in_acl, HTTPContext *c)
{
enum IPAddressAction last_action = IP_DENY;
IPAddressACL *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(FFStream *stream, HTTPContext *c)
{
int ret = 0;
IPAddressACL *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;
}
Fabrice Bellard
committed
/* compute the real filename of a file by matching it without its
extensions to all the stream filenames */
static void compute_real_filename(char *filename, int max_size)
{
char file1[1024];
char file2[1024];
char *p;
FFStream *stream;
/* 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 = first_stream; stream != NULL; 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 */
static int http_parse_request(HTTPContext *c)
{
char *p;
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;
FFStream *stream;
char ratebuf[32];
Fabrice Bellard
committed
get_word(cmd, sizeof(cmd), (const char **)&p);
av_strlcpy(c->method, cmd, sizeof(c->method));
Alex Beregszaszi
committed
c->post = 0;
Alex Beregszaszi
committed
c->post = 1;
Fabrice Bellard
committed
get_word(url, sizeof(url), (const char **)&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));
if (ffserver_debug)
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 */
Alex Beregszaszi
committed
p = strchr(url, '?');
av_strlcpy(info, p, sizeof(info));
av_strlcpy(filename, url + ((*url == '/') ? 1 : 0), sizeof(filename)-1);
Alex Beregszaszi
committed
for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
if (strncasecmp(p, "User-Agent:", 11) == 0) {
useragent = p + 11;
if (*useragent && *useragent != '\n' && 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 || strncasecmp(useragent, "NSPlayer", 8) != 0)) {
/* 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);
Alex Beregszaszi
committed
// "redirect" / request to index.html
if (!strlen(filename))
av_strlcpy(filename, "index.html", sizeof(filename) - 1);
Alex Beregszaszi
committed
if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
break;
stream = stream->next;
}
if (stream == NULL) {
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;
q += 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;
}
Alex Beregszaszi
committed
if (c->post == 0 && max_bandwidth < current_bandwidth) {
"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, 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) {
char *hostinfo = 0;
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
if (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 (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:
q += 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:
q += 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:
q += 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';
q += snprintf(q, c->buffer_size,
"HTTP/1.0 200 RTSP Redirect follows\r\n"
/* XXX: incorrect mime type ? */
"Content-type: application/x-rtsp\r\n"
"\r\n"
"rtsp://%s:%d/%s\r\n", hostname, ntohs(my_rtsp_addr.sin_port), filename);
Fabrice Bellard
committed
}
break;
case REDIR_SDP:
{
Fabrice Bellard
committed
int sdp_data_size, len;
struct sockaddr_in my_addr;
q += 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);
getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
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 in handy one day. */
char *logline = 0;
int client_id = 0;
for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
if (strncasecmp(p, "Pragma: log-line=", 17) == 0) {
logline = p;
break;
}
if (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);
goto send_error;
}
/* prepare http header */
q = c->buffer;
Philip Gladstone
committed
q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
mime_type = "application/x-octet-stream";
Philip Gladstone
committed
q += snprintf(q, q - (char *) 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);
Philip Gladstone
committed
q += snprintf(q, q - (char *) 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);
Philip Gladstone
committed
q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-Type: %s\r\n", mime_type);
q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
/* 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;
q += 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);
c->http_error = 200; /* horrible : we use this value to avoid
going to the send data state */
c->state = HTTPSTATE_SEND_HEADER;
return 0;
}
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 void compute_status(HTTPContext *c)
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: %s\r\n", "text/html");
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");
char sfilename[1024];
char *eosf;
if (stream->feed != stream) {
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")) {
Fabrice Bellard
committed
/* generate a sample RTSP director if
unicast. Generate an SDP redirector if
multicast */
Fabrice Bellard
committed
eosf = strrchr(sfilename, '.');
if (!eosf)
eosf = sfilename + strlen(sfilename);
Fabrice Bellard
committed
if (stream->is_multicast)
strcpy(eosf, ".sdp");
else
strcpy(eosf, ".rtsp");
}
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);
Fabrice Bellard
committed
fmt_bytecount(pb, stream->bytes_served);
switch(stream->stream_type) {
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];
Michael Niedermayer
committed
AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
switch(st->codec->codec_type) {
case AVMEDIA_TYPE_AUDIO:
Michael Niedermayer
committed
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:
Michael Niedermayer
committed
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:
Michael Niedermayer
committed
video_bit_rate += st->codec->bit_rate;
Fabrice Bellard
committed
break;
default:
Philip Gladstone
committed
}
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);
avio_printf(pb, "<td>%s", stream->feed->filename);
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 = first_stream;
while (stream != NULL) {
if (stream->feed == stream) {
avio_printf(pb, "<h2>Feed %s</h2>", stream->filename);
avio_printf(pb, "Running as pid %d.\n", stream->pid);
Fabrice Bellard
committed
#if defined(linux) && !defined(CONFIG_NOCUTILS)
{
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 %d",
Fabrice Bellard
committed
stream->pid);