Newer
Older
last_action = acl->action;
}
/* Nothing matched, so return not the last action */
return (last_action == IP_DENY) ? 1 : 0;
}
/* parse http request and prepare header */
static int http_parse_request(HTTPContext *c)
{
char *p;
int post;
int doing_asx;
Fabrice Bellard
committed
int doing_rtsp_redirector;
char cmd[32];
char info[1024], *filename;
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);
pstrcpy(c->method, sizeof(c->method), cmd);
if (!strcmp(cmd, "GET"))
post = 0;
else if (!strcmp(cmd, "POST"))
post = 1;
else
return -1;
Fabrice Bellard
committed
get_word(url, sizeof(url), (const char **)&p);
pstrcpy(c->url, sizeof(c->url), 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;
pstrcpy(c->protocol, sizeof(c->protocol), protocol);
/* find the filename and the optional info string in the request */
p = url;
if (*p == '/')
p++;
filename = p;
p = strchr(p, '?');
if (p) {
pstrcpy(info, sizeof(info), p);
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++;
}
if (strlen(filename) > 4 && strcmp(".asx", filename + strlen(filename) - 4) == 0) {
doing_asx = 1;
filename[strlen(filename)-1] = 'f';
} else {
doing_asx = 0;
}
if (strlen(filename) > 4 && strcmp(".asf", filename + strlen(filename) - 4) == 0 &&
(!useragent || strncasecmp(useragent, "NSPlayer", 8) != 0)) {
/* if this isn't WMP or lookalike, return the redirector file */
doing_asf_redirector = 1;
} else {
doing_asf_redirector = 0;
}
if (strlen(filename) > 4 &&
(strcmp(".rpm", filename + strlen(filename) - 4) == 0 ||
strcmp(".ram", filename + strlen(filename) - 4) == 0)) {
doing_ram = 1;
strcpy(filename + strlen(filename)-2, "m");
} else {
doing_ram = 0;
}
Fabrice Bellard
committed
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
if (strlen(filename) > 5 &&
strcmp(".rtsp", filename + strlen(filename) - 5) == 0) {
char file1[1024];
char file2[1024];
char *p;
doing_rtsp_redirector = 1;
/* compute filename by matching without the file extensions */
pstrcpy(file1, sizeof(file1), filename);
p = strrchr(file1, '.');
if (p)
*p = '\0';
for(stream = first_stream; stream != NULL; stream = stream->next) {
pstrcpy(file2, sizeof(file2), stream->filename);
p = strrchr(file2, '.');
if (p)
*p = '\0';
if (!strcmp(file1, file2)) {
pstrcpy(url, sizeof(url), stream->filename);
filename = url;
break;
}
}
} else {
doing_rtsp_redirector = 0;
}
if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
break;
stream = stream->next;
}
if (stream == NULL) {
sprintf(msg, "File '%s' not found", url);
goto send_error;
}
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
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 += sprintf(q, "HTTP/1.0 301 Moved\r\n");
q += sprintf(q, "Location: %s\r\n", stream->feed_filename);
q += sprintf(q, "Content-type: text/html\r\n");
q += sprintf(q, "\r\n");
q += sprintf(q, "<html><head><title>Moved</title></head><body>\r\n");
q += sprintf(q, "You should be <a href=\"%s\">redirected</a>.\r\n", stream->feed_filename);
q += sprintf(q, "</body></html>\r\n");
/* 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)) {
if (modify_current_stream(c, ratebuf)) {
for (i = 0; i < sizeof(c->feed_streams) / sizeof(c->feed_streams[0]); i++) {
if (c->switch_feed_streams[i] >= 0)
do_switch_stream(c, i);
}
}
}
if (post == 0 && stream->stream_type == STREAM_TYPE_LIVE) {
/* See if we meet the bandwidth requirements */
for(i=0;i<stream->nb_streams;i++) {
AVStream *st = stream->streams[i];
switch(st->codec.codec_type) {
case CODEC_TYPE_AUDIO:
c->bandwidth += st->codec.bit_rate;
break;
case CODEC_TYPE_VIDEO:
c->bandwidth += st->codec.bit_rate;
break;
default:
av_abort();
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
}
}
}
c->bandwidth /= 1000;
nb_bandwidth += c->bandwidth;
if (post == 0 && nb_max_bandwidth < nb_bandwidth) {
c->http_error = 200;
q = c->buffer;
q += sprintf(q, "HTTP/1.0 200 Server too busy\r\n");
q += sprintf(q, "Content-type: text/html\r\n");
q += sprintf(q, "\r\n");
q += sprintf(q, "<html><head><title>Too busy</title></head><body>\r\n");
q += sprintf(q, "The server is too busy to serve your request at this time.<p>\r\n");
q += sprintf(q, "The bandwidth being served (including your stream) is %dkbit/sec, and this exceeds the limit of %dkbit/sec\r\n",
nb_bandwidth, nb_max_bandwidth);
q += sprintf(q, "</body></html>\r\n");
/* prepare output buffer */
c->buffer_ptr = c->buffer;
c->buffer_end = q;
c->state = HTTPSTATE_SEND_HEADER;
return 0;
}
Fabrice Bellard
committed
if (doing_asx || doing_ram || doing_asf_redirector ||
doing_rtsp_redirector) {
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
char *hostinfo = 0;
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;
if (doing_asx) {
q += sprintf(q, "HTTP/1.0 200 ASX Follows\r\n");
q += sprintf(q, "Content-type: video/x-ms-asf\r\n");
q += sprintf(q, "\r\n");
q += sprintf(q, "<ASX Version=\"3\">\r\n");
q += sprintf(q, "<!-- Autogenerated by ffserver -->\r\n");
q += sprintf(q, "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n",
hostbuf, filename, info);
q += sprintf(q, "</ASX>\r\n");
} else if (doing_ram) {
q += sprintf(q, "HTTP/1.0 200 RAM Follows\r\n");
q += sprintf(q, "Content-type: audio/x-pn-realaudio\r\n");
q += sprintf(q, "\r\n");
q += sprintf(q, "# Autogenerated by ffserver\r\n");
q += sprintf(q, "http://%s/%s%s\r\n",
hostbuf, filename, info);
} else if (doing_asf_redirector) {
q += sprintf(q, "HTTP/1.0 200 ASF Redirect follows\r\n");
q += sprintf(q, "Content-type: video/x-ms-asf\r\n");
q += sprintf(q, "\r\n");
q += sprintf(q, "[Reference]\r\n");
q += sprintf(q, "Ref1=http://%s/%s%s\r\n",
hostbuf, filename, info);
Fabrice Bellard
committed
} else if (doing_rtsp_redirector) {
char hostname[256], *p;
/* extract only hostname */
pstrcpy(hostname, sizeof(hostname), hostbuf);
p = strrchr(hostname, ':');
if (p)
*p = '\0';
q += sprintf(q, "HTTP/1.0 200 RTSP Redirect follows\r\n");
/* XXX: incorrect mime type ? */
q += sprintf(q, "Content-type: application/x-rtsp\r\n");
q += sprintf(q, "\r\n");
q += sprintf(q, "rtsp://%s:%d/%s\r\n",
hostname, ntohs(my_rtsp_addr.sin_port),
filename);
} else {
av_abort();
Fabrice Bellard
committed
}
/* prepare output buffer */
c->buffer_ptr = c->buffer;
c->buffer_end = q;
c->state = HTTPSTATE_SEND_HEADER;
return 0;
}
}
}
sprintf(msg, "ASX/RAM file not handled");
goto send_error;
stream->conns_served++;
/* XXX: add there authenticate and IP match */
if (post) {
/* if post, it means a feed is being sent */
if (!stream->is_feed) {
/* However it might be a status report from WMP! Lets 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--;
http_log("%.*s\n", eol - logline, logline);
c->suppress_log = 1;
}
}
#ifdef DEBUG_WMP
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) {
if (modify_current_stream(wmpc, ratebuf)) {
wmpc->switch_pending = 1;
}
}
}
sprintf(msg, "POST command not handled");
goto send_error;
}
if (http_start_receive_data(c) < 0) {
sprintf(msg, "could not open feed");
goto send_error;
}
c->http_error = 0;
c->state = HTTPSTATE_RECEIVE_DATA;
return 0;
}
if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0) {
http_log("\nGot request:\n%s\n", c->buffer);
}
#endif
if (c->stream->stream_type == STREAM_TYPE_STATUS)
goto send_stats;
/* open input stream */
if (open_input_stream(c, info) < 0) {
sprintf(msg, "Input stream corresponding to '%s' not found", url);
goto send_error;
}
/* prepare http header */
q = c->buffer;
q += sprintf(q, "HTTP/1.0 200 OK\r\n");
mime_type = c->stream->fmt->mime_type;
if (!mime_type)
mime_type = "application/x-octet_stream";
q += sprintf(q, "Pragma: no-cache\r\n");
/* for asf, we need extra headers */
if (!strcmp(c->stream->fmt->name,"asf_stream")) {
/* Need to allocate a client id */
c->wmp_client_id = random() & 0x7fffffff;
q += sprintf(q, "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 += sprintf(q, "Content-Type: %s\r\n", mime_type);
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
q += sprintf(q, "\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 += sprintf(q, "HTTP/1.0 404 Not Found\r\n");
q += sprintf(q, "Content-type: %s\r\n", "text/html");
q += sprintf(q, "\r\n");
q += sprintf(q, "<HTML>\n");
q += sprintf(q, "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n");
q += sprintf(q, "<BODY>%s</BODY>\n", msg);
q += sprintf(q, "</HTML>\n");
/* prepare output buffer */
c->buffer_ptr = c->buffer;
c->buffer_end = q;
c->state = HTTPSTATE_SEND_HEADER;
return 0;
send_stats:
compute_stats(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;
}
Fabrice Bellard
committed
static void fmt_bytecount(ByteIOContext *pb, INT64 count)
{
static const char *suffix = " kMGTP";
const char *s;
for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++) {
}
Fabrice Bellard
committed
url_fprintf(pb, "%lld%c", count, *s);
}
static void compute_stats(HTTPContext *c)
{
HTTPContext *c1;
FFStream *stream;
Fabrice Bellard
committed
char *p;
Fabrice Bellard
committed
int i, len;
ByteIOContext pb1, *pb = &pb1;
Fabrice Bellard
committed
if (url_open_dyn_buf(pb) < 0) {
/* XXX: return an error ? */
Fabrice Bellard
committed
c->buffer_end = c->buffer;
return;
Fabrice Bellard
committed
url_fprintf(pb, "HTTP/1.0 200 OK\r\n");
url_fprintf(pb, "Content-type: %s\r\n", "text/html");
url_fprintf(pb, "Pragma: no-cache\r\n");
url_fprintf(pb, "\r\n");
Fabrice Bellard
committed
url_fprintf(pb, "<HEAD><TITLE>FFServer Status</TITLE>\n");
Fabrice Bellard
committed
url_fprintf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
Fabrice Bellard
committed
url_fprintf(pb, "</HEAD>\n<BODY>");
url_fprintf(pb, "<H1>FFServer Status</H1>\n");
Fabrice Bellard
committed
url_fprintf(pb, "<H2>Available Streams</H2>\n");
url_fprintf(pb, "<TABLE cellspacing=0 cellpadding=4>\n");
url_fprintf(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) {
Fabrice Bellard
committed
pstrcpy(sfilename, sizeof(sfilename) - 10, stream->filename);
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");
Fabrice Bellard
committed
} else if (stream->fmt == &rtp_mux) {
/* generate a sample RTSP director - maybe should
generate a .sdp file ? */
eosf = strrchr(sfilename, '.');
if (!eosf)
eosf = sfilename + strlen(sfilename);
strcpy(eosf, ".rtsp");
}
Fabrice Bellard
committed
url_fprintf(pb, "<TR><TD><A HREF=\"/%s\">%s</A> ",
sfilename, stream->filename);
Fabrice Bellard
committed
url_fprintf(pb, "<td align=right> %d <td align=right> ",
stream->conns_served);
Fabrice Bellard
committed
fmt_bytecount(pb, stream->bytes_served);
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
switch(stream->stream_type) {
case STREAM_TYPE_LIVE:
{
int audio_bit_rate = 0;
int video_bit_rate = 0;
char *audio_codec_name = "";
char *video_codec_name = "";
char *audio_codec_name_extra = "";
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 CODEC_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 CODEC_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;
default:
av_abort();
Philip Gladstone
committed
}
Fabrice Bellard
committed
url_fprintf(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,
(audio_bit_rate + video_bit_rate) / 1000,
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) {
Fabrice Bellard
committed
url_fprintf(pb, "<TD>%s", stream->feed->filename);
} else {
Fabrice Bellard
committed
url_fprintf(pb, "<TD>%s", stream->feed_filename);
}
Fabrice Bellard
committed
url_fprintf(pb, "\n");
break;
default:
Fabrice Bellard
committed
url_fprintf(pb, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
break;
Fabrice Bellard
committed
url_fprintf(pb, "</TABLE>\n");
stream = first_stream;
while (stream != NULL) {
if (stream->feed == stream) {
Fabrice Bellard
committed
url_fprintf(pb, "<h2>Feed %s</h2>", stream->filename);
Fabrice Bellard
committed
url_fprintf(pb, "Running as pid %d.\n", stream->pid);
Fabrice Bellard
committed
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
#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",
stream->pid);
pid_stat = popen(ps_cmd, "r");
if (pid_stat) {
char cpuperc[10];
char cpuused[64];
if (fscanf(pid_stat, "%10s %64s", cpuperc,
cpuused) == 2) {
url_fprintf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
cpuperc, cpuused);
}
fclose(pid_stat);
Fabrice Bellard
committed
url_fprintf(pb, "<p>");
Fabrice Bellard
committed
url_fprintf(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->nb_streams; i++) {
AVStream *st = stream->streams[i];
AVCodec *codec = avcodec_find_encoder(st->codec.codec_id);
char *type = "unknown";
Philip Gladstone
committed
char parameters[64];
parameters[0] = 0;
switch(st->codec.codec_type) {
case CODEC_TYPE_AUDIO:
type = "audio";
break;
case CODEC_TYPE_VIDEO:
type = "video";
sprintf(parameters, "%dx%d, q=%d-%d, fps=%d", st->codec.width, st->codec.height,
st->codec.qmin, st->codec.qmax, st->codec.frame_rate / FRAME_RATE_BASE);
break;
default:
av_abort();
}
Fabrice Bellard
committed
url_fprintf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
Philip Gladstone
committed
i, type, st->codec.bit_rate/1000, codec ? codec->name : "", parameters);
}
Fabrice Bellard
committed
url_fprintf(pb, "</table>\n");
}
stream = stream->next;
}
#if 0
{
float avg;
AVCodecContext *enc;
char buf[1024];
/* feed status */
stream = first_feed;
while (stream != NULL) {
Fabrice Bellard
committed
url_fprintf(pb, "<H1>Feed '%s'</H1>\n", stream->filename);
url_fprintf(pb, "<TABLE>\n");
url_fprintf(pb, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n");
for(i=0;i<stream->nb_streams;i++) {
AVStream *st = stream->streams[i];
FeedData *fdata = st->priv_data;
enc = &st->codec;
avcodec_string(buf, sizeof(buf), enc);
avg = fdata->avg_frame_size * (float)enc->rate * 8.0;
if (enc->codec->type == CODEC_TYPE_AUDIO && enc->frame_size > 0)
avg /= enc->frame_size;
Fabrice Bellard
committed
url_fprintf(pb, "<TR><TD>%s <TD> %d <TD> %Ld <TD> %0.1f\n",
buf, enc->frame_number, fdata->data_count, avg / 1000.0);
}
Fabrice Bellard
committed
url_fprintf(pb, "</TABLE>\n");
stream = stream->next_feed;
}
}
#endif
/* connection status */
Fabrice Bellard
committed
url_fprintf(pb, "<H2>Connection Status</H2>\n");
Fabrice Bellard
committed
url_fprintf(pb, "Number of connections: %d / %d<BR>\n",
Fabrice Bellard
committed
url_fprintf(pb, "Bandwidth in use: %dk / %dk<BR>\n",
nb_bandwidth, nb_max_bandwidth);
Fabrice Bellard
committed
url_fprintf(pb, "<TABLE>\n");
url_fprintf(pb, "<TR><th>#<th>File<th>IP<th>Proto<th>State<th>Target bits/sec<th>Actual bits/sec<th>Bytes transferred\n");
Fabrice Bellard
committed
while (c1 != NULL) {
int bitrate;
int j;
bitrate = 0;
Fabrice Bellard
committed
if (c1->stream) {
for (j = 0; j < c1->stream->nb_streams; j++) {
if (!c1->stream->feed) {
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;
}
}
Fabrice Bellard
committed
url_fprintf(pb, "<TR><TD><B>%d</B><TD>%s%s<TD>%s<TD>%s<TD>%s<td align=right>",
i,
c1->stream ? c1->stream->filename : "",
c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
p,
c1->protocol,
http_state[c1->state]);
fmt_bytecount(pb, bitrate);
url_fprintf(pb, "<td align=right>");
fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
url_fprintf(pb, "<td align=right>");
fmt_bytecount(pb, c1->data_count);
url_fprintf(pb, "\n");
Fabrice Bellard
committed
url_fprintf(pb, "</TABLE>\n");
Fabrice Bellard
committed
url_fprintf(pb, "<HR size=1 noshade>Generated at %s", p);
url_fprintf(pb, "</BODY>\n</HTML>\n");
Fabrice Bellard
committed
len = url_close_dyn_buf(pb, &c->pb_buffer);
c->buffer_ptr = c->pb_buffer;
c->buffer_end = c->pb_buffer + len;
Fabrice Bellard
committed
/* check if the parser needs to be opened for stream i */
static void open_parser(AVFormatContext *s, int i)
Fabrice Bellard
committed
AVStream *st = s->streams[i];
AVCodec *codec;
Fabrice Bellard
committed
if (!st->codec.codec) {
codec = avcodec_find_decoder(st->codec.codec_id);
if (codec && (codec->capabilities & CODEC_CAP_PARSE_ONLY)) {
st->codec.parse_only = 1;
if (avcodec_open(&st->codec, codec) < 0) {
st->codec.parse_only = 0;
}
}
static int open_input_stream(HTTPContext *c, const char *info)
{
char buf[128];
char input_filename[1024];
AVFormatContext *s;
Fabrice Bellard
committed
int buf_size, i;
INT64 stream_pos;
/* find file name */
if (c->stream->feed) {
strcpy(input_filename, c->stream->feed->feed_filename);
buf_size = FFM_PACKET_SIZE;
/* compute position (absolute time) */
if (find_info_tag(buf, sizeof(buf), "date", info)) {
stream_pos = parse_date(buf, 0);
Philip Gladstone
committed
} else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
int prebuffer = strtol(buf, 0, 10);
stream_pos = av_gettime() - prebuffer * (INT64)1000000;
stream_pos = av_gettime() - c->stream->prebuffer * (INT64)1000;
}
} else {
strcpy(input_filename, c->stream->feed_filename);
buf_size = 0;
/* compute position (relative time) */
if (find_info_tag(buf, sizeof(buf), "date", info)) {
stream_pos = parse_date(buf, 1);
} else {
stream_pos = 0;
}
}
if (input_filename[0] == '\0')
return -1;
#if 0
{ time_t when = stream_pos / 1000000;
http_log("Stream pos = %lld, time=%s", stream_pos, ctime(&when));
}
#endif
Fabrice Bellard
committed
if (av_open_input_file(&s, input_filename, NULL, buf_size, NULL) < 0) {
http_log("%s not found", input_filename);
Fabrice Bellard
committed
}
Fabrice Bellard
committed
/* open each parser */
for(i=0;i<s->nb_streams;i++)
open_parser(s, i);
/* choose stream as clock source (we favorize video stream if
present) for packet sending */
c->pts_stream_index = 0;
for(i=0;i<c->stream->nb_streams;i++) {
if (c->pts_stream_index == 0 &&
c->stream->streams[i]->codec.codec_type == CODEC_TYPE_VIDEO) {
c->pts_stream_index = i;
}
}
if (c->fmt_in->iformat->read_seek) {
c->fmt_in->iformat->read_seek(c->fmt_in, stream_pos);
Fabrice Bellard
committed
/* set the start time (needed for maxtime and RTP packet timing) */
c->start_time = cur_time;
c->first_pts = AV_NOPTS_VALUE;
Fabrice Bellard
committed
/* currently desactivated because the new PTS handling is not
satisfactory yet */
//#define AV_READ_FRAME
#ifdef AV_READ_FRAME
Fabrice Bellard
committed
/* XXX: generalize that in ffmpeg for picture/audio/data. Currently
the return packet MUST NOT be freed */
int av_read_frame(AVFormatContext *s, AVPacket *pkt)
{
AVStream *st;
int len, ret, old_nb_streams, i;
Philip Gladstone
committed
Fabrice Bellard
committed
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
/* see if remaining frames must be parsed */
for(;;) {
if (s->cur_len > 0) {
st = s->streams[s->cur_pkt.stream_index];
len = avcodec_parse_frame(&st->codec, &pkt->data, &pkt->size,
s->cur_ptr, s->cur_len);
if (len < 0) {
/* error: get next packet */
s->cur_len = 0;
} else {
s->cur_ptr += len;
s->cur_len -= len;
if (pkt->size) {
/* init pts counter if not done */
if (st->pts.den == 0) {
switch(st->codec.codec_type) {
case CODEC_TYPE_AUDIO:
st->pts_incr = (INT64)s->pts_den;
av_frac_init(&st->pts, st->pts.val, 0,
(INT64)s->pts_num * st->codec.sample_rate);
break;
case CODEC_TYPE_VIDEO:
st->pts_incr = (INT64)s->pts_den * FRAME_RATE_BASE;
av_frac_init(&st->pts, st->pts.val, 0,
(INT64)s->pts_num * st->codec.frame_rate);
break;
default:
av_abort();
}
}
/* a frame was read: return it */
pkt->pts = st->pts.val;
#if 0
printf("add pts=%Lx num=%Lx den=%Lx incr=%Lx\n",
st->pts.val, st->pts.num, st->pts.den, st->pts_incr);
#endif
switch(st->codec.codec_type) {
case CODEC_TYPE_AUDIO:
av_frac_add(&st->pts, st->pts_incr * st->codec.frame_size);
break;
case CODEC_TYPE_VIDEO:
av_frac_add(&st->pts, st->pts_incr);
break;
default:
av_abort();
}
pkt->stream_index = s->cur_pkt.stream_index;
/* we use the codec indication because it is
more accurate than the demux flags */
pkt->flags = 0;
if (st->codec.key_frame)
pkt->flags |= PKT_FLAG_KEY;
return 0;
}
Fabrice Bellard
committed
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
/* free previous packet */
av_free_packet(&s->cur_pkt);
old_nb_streams = s->nb_streams;
ret = av_read_packet(s, &s->cur_pkt);
if (ret)
return ret;
/* open parsers for each new streams */
for(i = old_nb_streams; i < s->nb_streams; i++)
open_parser(s, i);
st = s->streams[s->cur_pkt.stream_index];
/* update current pts (XXX: dts handling) from packet, or
use current pts if none given */
if (s->cur_pkt.pts != AV_NOPTS_VALUE) {
av_frac_set(&st->pts, s->cur_pkt.pts);
} else {
s->cur_pkt.pts = st->pts.val;
}
if (!st->codec.codec) {
/* no codec opened: just return the raw packet */
*pkt = s->cur_pkt;
/* no codec opened: just update the pts by considering we
have one frame and free the packet */
if (st->pts.den == 0) {
switch(st->codec.codec_type) {
case CODEC_TYPE_AUDIO:
st->pts_incr = (INT64)s->pts_den * st->codec.frame_size;
av_frac_init(&st->pts, st->pts.val, 0,
(INT64)s->pts_num * st->codec.sample_rate);
break;
case CODEC_TYPE_VIDEO:
st->pts_incr = (INT64)s->pts_den * FRAME_RATE_BASE;
av_frac_init(&st->pts, st->pts.val, 0,
(INT64)s->pts_num * st->codec.frame_rate);
break;
default:
av_abort();
}
}
av_frac_add(&st->pts, st->pts_incr);
return 0;
} else {
s->cur_ptr = s->cur_pkt.data;
s->cur_len = s->cur_pkt.size;
Fabrice Bellard
committed
1919
1920
1921
1922
1923
1924
1925
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
1954
1955
1956
}
}
static int compute_send_delay(HTTPContext *c)
{
INT64 cur_pts, delta_pts, next_pts;
int delay1;
/* compute current pts value from system time */
cur_pts = ((INT64)(cur_time - c->start_time) * c->fmt_in->pts_den) /
(c->fmt_in->pts_num * 1000LL);
/* compute the delta from the stream we choose as
main clock (we do that to avoid using explicit
buffers to do exact packet reordering for each
stream */
/* XXX: really need to fix the number of streams */
if (c->pts_stream_index >= c->fmt_in->nb_streams)
next_pts = cur_pts;
else
next_pts = c->fmt_in->streams[c->pts_stream_index]->pts.val;
delta_pts = next_pts - cur_pts;
if (delta_pts <= 0) {
delay1 = 0;
} else {
delay1 = (delta_pts * 1000 * c->fmt_in->pts_num) / c->fmt_in->pts_den;
}
return delay1;
}
#else
/* just fall backs */
int av_read_frame(AVFormatContext *s, AVPacket *pkt)
{
return av_read_packet(s, pkt);
}
static int compute_send_delay(HTTPContext *c)
{
int datarate = 8 * get_longterm_datarate(&c->datarate, c->data_count);
if (datarate > c->bandwidth * 2000) {
return 1000;
}
Fabrice Bellard
committed
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
return 0;
}
#endif
static int http_prepare_data(HTTPContext *c)
{
int i, len, ret;
AVFormatContext *ctx;
switch(c->state) {
case HTTPSTATE_SEND_DATA_HEADER:
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);
/* open output stream by using specified codecs */
c->fmt_ctx.oformat = c->stream->fmt;
c->fmt_ctx.nb_streams = c->stream->nb_streams;
for(i=0;i<c->fmt_ctx.nb_streams;i++) {
AVStream *st;
st = av_mallocz(sizeof(AVStream));
c->fmt_ctx.streams[i] = st;
/* if file or feed, then just take streams from FFStream struct */
if (!c->stream->feed ||
c->stream->feed == c->stream)
memcpy(st, c->stream->streams[i], sizeof(AVStream));
else
memcpy(st, c->stream->feed->streams[c->stream->feed_streams[i]],
sizeof(AVStream));
st->codec.frame_number = 0; /* XXX: should be done in
AVStream, not in codec */
}