Newer
Older
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
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
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;
}
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
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();
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
}
}
}
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) {
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
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);
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
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);
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
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
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
#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
1806
1807
1808
1809
1810
1811
1812
1813
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
/* 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
1863
1864
1865
1866
1867
1868
1869
1870
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
/* 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
1911
1912
1913
1914
1915
1916
1917
1918
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
}
}
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
1954
1955
1956
1957
1958
1959
1960
1961
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 */
}
c->got_key_frame = 0;
/* prepare header and save header data in a stream */
if (url_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
/* XXX: potential leak */
return -1;
}
c->fmt_ctx.pb.is_streamed = 1;