|
|
|
@ -31,7 +31,7 @@ |
|
|
|
|
#include <stdlib.h> |
|
|
|
|
#include <stdio.h> |
|
|
|
|
#include "libavformat/avformat.h" |
|
|
|
|
// FIXME those are internal headers, ffserver _really_ shouldn't use them
|
|
|
|
|
/* FIXME: those are internal headers, ffserver _really_ shouldn't use them */ |
|
|
|
|
#include "libavformat/ffm.h" |
|
|
|
|
#include "libavformat/network.h" |
|
|
|
|
#include "libavformat/os_support.h" |
|
|
|
@ -251,7 +251,8 @@ static unsigned int nb_connections; |
|
|
|
|
|
|
|
|
|
static uint64_t current_bandwidth; |
|
|
|
|
|
|
|
|
|
static int64_t cur_time; // Making this global saves on passing it around everywhere
|
|
|
|
|
/* Making this global saves on passing it around everywhere */ |
|
|
|
|
static int64_t cur_time; |
|
|
|
|
|
|
|
|
|
static AVLFG random_state; |
|
|
|
|
|
|
|
|
@ -630,9 +631,8 @@ static int http_server(void) |
|
|
|
|
poll_entry++; |
|
|
|
|
} else { |
|
|
|
|
/* when ffserver is doing the timing, we work by
|
|
|
|
|
looking at which packet needs to be sent every |
|
|
|
|
10 ms */ |
|
|
|
|
/* one tick wait XXX: 10 ms assumed */ |
|
|
|
|
* looking at which packet needs to be sent every |
|
|
|
|
* 10 ms (one tick wait XXX: 10 ms assumed) */ |
|
|
|
|
if (delay > 10) |
|
|
|
|
delay = 10; |
|
|
|
|
} |
|
|
|
@ -655,7 +655,7 @@ static int http_server(void) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* wait for an event on one connection. We poll at least every
|
|
|
|
|
second to handle timeouts */ |
|
|
|
|
* second to handle timeouts */ |
|
|
|
|
do { |
|
|
|
|
ret = poll(poll_table, poll_entry - poll_table, delay); |
|
|
|
|
if (ret < 0 && ff_neterrno() != AVERROR(EAGAIN) && |
|
|
|
@ -900,11 +900,11 @@ static int handle_connection(HTTPContext *c) |
|
|
|
|
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) { |
|
|
|
|
if (c->state == HTTPSTATE_WAIT_REQUEST) |
|
|
|
|
ret = http_parse_request(c); |
|
|
|
|
} else { |
|
|
|
|
else |
|
|
|
|
ret = rtsp_parse_request(c); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (ret < 0) |
|
|
|
|
return -1; |
|
|
|
|
} else if (ptr >= c->buffer_end) { |
|
|
|
@ -949,8 +949,8 @@ static int handle_connection(HTTPContext *c) |
|
|
|
|
case HTTPSTATE_SEND_DATA_HEADER: |
|
|
|
|
case HTTPSTATE_SEND_DATA_TRAILER: |
|
|
|
|
/* 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 */ |
|
|
|
|
* input streams set the speed). It may be better to verify |
|
|
|
|
* that we do not rely too much on the kernel queues */ |
|
|
|
|
if (!c->is_packetized) { |
|
|
|
|
if (c->poll_entry->revents & (POLLERR | POLLHUP)) |
|
|
|
|
return -1; |
|
|
|
@ -1277,8 +1277,10 @@ static int validate_acl(FFServerStream *stream, HTTPContext *c) |
|
|
|
|
return ret; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* compute the real filename of a file by matching it without its
|
|
|
|
|
extensions to all the stream's filenames */ |
|
|
|
|
/**
|
|
|
|
|
* compute the real filename of a file by matching it without its |
|
|
|
|
* extensions to all the stream's filenames |
|
|
|
|
*/ |
|
|
|
|
static void compute_real_filename(char *filename, int max_size) |
|
|
|
|
{ |
|
|
|
|
char file1[1024]; |
|
|
|
@ -1396,7 +1398,7 @@ static int http_parse_request(HTTPContext *c) |
|
|
|
|
compute_real_filename(filename, sizeof(filename) - 1); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// "redirect" / request to index.html
|
|
|
|
|
/* "redirect" request to index.html */ |
|
|
|
|
if (!strlen(filename)) |
|
|
|
|
av_strlcpy(filename, "index.html", sizeof(filename) - 1); |
|
|
|
|
|
|
|
|
@ -1735,8 +1737,9 @@ static int http_parse_request(HTTPContext *c) |
|
|
|
|
return 0; |
|
|
|
|
send_status: |
|
|
|
|
compute_status(c); |
|
|
|
|
c->http_error = 200; /* horrible : we use this value to avoid
|
|
|
|
|
going to the send data state */ |
|
|
|
|
/* horrible: we use this value to avoid
|
|
|
|
|
* going to the send data state */ |
|
|
|
|
c->http_error = 200; |
|
|
|
|
c->state = HTTPSTATE_SEND_HEADER; |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
@ -1847,8 +1850,8 @@ static void compute_status(HTTPContext *c) |
|
|
|
|
strcpy(eosf - 3, ".ram"); |
|
|
|
|
else if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) { |
|
|
|
|
/* generate a sample RTSP director if
|
|
|
|
|
unicast. Generate an SDP redirector if |
|
|
|
|
multicast */ |
|
|
|
|
* unicast. Generate an SDP redirector if |
|
|
|
|
* multicast */ |
|
|
|
|
eosf = strrchr(sfilename, '.'); |
|
|
|
|
if (!eosf) |
|
|
|
|
eosf = sfilename + strlen(sfilename); |
|
|
|
@ -2119,8 +2122,7 @@ static int64_t get_server_clock(HTTPContext *c) |
|
|
|
|
return (cur_time - c->start_time) * 1000; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* return the estimated time at which the current packet must be sent
|
|
|
|
|
(in us) */ |
|
|
|
|
/* return the estimated time (in us) at which the current packet must be sent */ |
|
|
|
|
static int64_t get_packet_send_clock(HTTPContext *c) |
|
|
|
|
{ |
|
|
|
|
int bytes_left, bytes_sent, frame_bytes; |
|
|
|
@ -2158,7 +2160,8 @@ static int http_prepare_data(HTTPContext *c) |
|
|
|
|
AVStream *src; |
|
|
|
|
c->fmt_ctx.streams[i] = av_mallocz(sizeof(AVStream)); |
|
|
|
|
|
|
|
|
|
/* if file or feed, then just take streams from FFServerStream struct */ |
|
|
|
|
/* if file or feed, then just take streams from FFServerStream
|
|
|
|
|
* struct */ |
|
|
|
|
if (!c->stream->feed || |
|
|
|
|
c->stream->feed == c->stream) |
|
|
|
|
src = c->stream->streams[i]; |
|
|
|
@ -2223,7 +2226,7 @@ static int http_prepare_data(HTTPContext *c) |
|
|
|
|
if (ret < 0) { |
|
|
|
|
if (c->stream->feed) { |
|
|
|
|
/* if coming from feed, it means we reached the end of the
|
|
|
|
|
ffm file, so must wait for more data */ |
|
|
|
|
* ffm file, so must wait for more data */ |
|
|
|
|
c->state = HTTPSTATE_WAIT_FEED; |
|
|
|
|
return 1; /* state changed */ |
|
|
|
|
} |
|
|
|
@ -2310,9 +2313,9 @@ static int http_prepare_data(HTTPContext *c) |
|
|
|
|
max_packet_size = c->rtp_handles[c->packet_stream_index]->max_packet_size; |
|
|
|
|
ret = ffio_open_dyn_packet_buf(&ctx->pb, |
|
|
|
|
max_packet_size); |
|
|
|
|
} else { |
|
|
|
|
} else |
|
|
|
|
ret = avio_open_dyn_buf(&ctx->pb); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (ret < 0) { |
|
|
|
|
/* XXX: potential leak */ |
|
|
|
|
return -1; |
|
|
|
@ -2375,7 +2378,8 @@ static int http_prepare_data(HTTPContext *c) |
|
|
|
|
|
|
|
|
|
/* should convert the format at the same time */ |
|
|
|
|
/* send data starting at c->buffer_ptr to the output connection
|
|
|
|
|
* (either UDP or TCP) */ |
|
|
|
|
* (either UDP or TCP) |
|
|
|
|
*/ |
|
|
|
|
static int http_send_data(HTTPContext *c) |
|
|
|
|
{ |
|
|
|
|
int len, ret; |
|
|
|
@ -2456,8 +2460,8 @@ static int http_send_data(HTTPContext *c) |
|
|
|
|
rtsp_c->packet_buffer_ptr += len; |
|
|
|
|
if (rtsp_c->packet_buffer_ptr < rtsp_c->packet_buffer_end) { |
|
|
|
|
/* if we could not send all the data, we will
|
|
|
|
|
send it later, so a new state is needed to |
|
|
|
|
"lock" the RTSP TCP connection */ |
|
|
|
|
* send it later, so a new state is needed to |
|
|
|
|
* "lock" the RTSP TCP connection */ |
|
|
|
|
rtsp_c->state = RTSPSTATE_SEND_PACKET; |
|
|
|
|
break; |
|
|
|
|
} else |
|
|
|
@ -2585,12 +2589,11 @@ static int http_receive_data(HTTPContext *c) |
|
|
|
|
goto fail; |
|
|
|
|
c->buffer_ptr = c->buffer; |
|
|
|
|
break; |
|
|
|
|
} else if (++loop_run > 10) { |
|
|
|
|
} else if (++loop_run > 10) |
|
|
|
|
/* no chunk header, abort */ |
|
|
|
|
goto fail; |
|
|
|
|
} else { |
|
|
|
|
else |
|
|
|
|
c->buffer_ptr++; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (c->buffer_end > c->buffer_ptr) { |
|
|
|
@ -2623,7 +2626,7 @@ static int http_receive_data(HTTPContext *c) |
|
|
|
|
if (c->buffer_ptr >= c->buffer_end) { |
|
|
|
|
FFServerStream *feed = c->stream; |
|
|
|
|
/* a packet has been received : write it in the store, except
|
|
|
|
|
if header */ |
|
|
|
|
* if header */ |
|
|
|
|
if (c->data_count > FFM_PACKET_SIZE) { |
|
|
|
|
/* XXX: use llseek or url_seek
|
|
|
|
|
* XXX: Should probably fail? */ |
|
|
|
@ -2829,10 +2832,10 @@ static int rtsp_parse_request(HTTPContext *c) |
|
|
|
|
the_end: |
|
|
|
|
len = avio_close_dyn_buf(c->pb, &c->pb_buffer); |
|
|
|
|
c->pb = NULL; /* safety */ |
|
|
|
|
if (len < 0) { |
|
|
|
|
if (len < 0) |
|
|
|
|
/* XXX: cannot do more */ |
|
|
|
|
return -1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
c->buffer_ptr = c->pb_buffer; |
|
|
|
|
c->buffer_end = c->pb_buffer + len; |
|
|
|
|
c->state = RTSPSTATE_SEND_REPLY; |
|
|
|
@ -2851,9 +2854,9 @@ static int prepare_sdp_description(FFServerStream *stream, uint8_t **pbuffer, |
|
|
|
|
*pbuffer = NULL; |
|
|
|
|
|
|
|
|
|
avc = avformat_alloc_context(); |
|
|
|
|
if (!avc || !rtp_format) { |
|
|
|
|
if (!avc || !rtp_format) |
|
|
|
|
return -1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
avc->oformat = rtp_format; |
|
|
|
|
av_dict_set(&avc->metadata, "title", |
|
|
|
|
entry ? entry->value : "No Title", 0); |
|
|
|
@ -2862,9 +2865,8 @@ static int prepare_sdp_description(FFServerStream *stream, uint8_t **pbuffer, |
|
|
|
|
snprintf(avc->filename, 1024, "rtp://%s:%d?multicast=1?ttl=%d", |
|
|
|
|
inet_ntoa(stream->multicast_ip), |
|
|
|
|
stream->multicast_port, stream->multicast_ttl); |
|
|
|
|
} else { |
|
|
|
|
} else |
|
|
|
|
snprintf(avc->filename, 1024, "rtp://0.0.0.0"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
avc->streams = av_malloc_array(avc->nb_streams, sizeof(*avc->streams)); |
|
|
|
|
if (!avc->streams) |
|
|
|
@ -2894,7 +2896,7 @@ static int prepare_sdp_description(FFServerStream *stream, uint8_t **pbuffer, |
|
|
|
|
|
|
|
|
|
static void rtsp_cmd_options(HTTPContext *c, const char *url) |
|
|
|
|
{ |
|
|
|
|
// rtsp_reply_header(c, RTSP_STATUS_OK);
|
|
|
|
|
/* rtsp_reply_header(c, RTSP_STATUS_OK); */ |
|
|
|
|
avio_printf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK"); |
|
|
|
|
avio_printf(c->pb, "CSeq: %d\r\n", c->seq); |
|
|
|
|
avio_printf(c->pb, "Public: %s\r\n", |
|
|
|
@ -3061,7 +3063,7 @@ static void rtsp_cmd_setup(HTTPContext *c, const char *url, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* test if stream is OK (test needed because several SETUP needs
|
|
|
|
|
to be done for a given file) */ |
|
|
|
|
* to be done for a given file) */ |
|
|
|
|
if (rtp_c->stream != stream) { |
|
|
|
|
rtsp_reply_error(c, RTSP_STATUS_SERVICE); |
|
|
|
|
return; |
|
|
|
@ -3122,8 +3124,10 @@ static void rtsp_cmd_setup(HTTPContext *c, const char *url, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* find an RTP connection by using the session ID. Check consistency
|
|
|
|
|
with filename */ |
|
|
|
|
/**
|
|
|
|
|
* find an RTP connection by using the session ID. Check consistency |
|
|
|
|
* with filename |
|
|
|
|
*/ |
|
|
|
|
static HTTPContext *find_rtp_session_with_url(const char *url, |
|
|
|
|
const char *session_id) |
|
|
|
|
{ |
|
|
|
@ -3146,10 +3150,10 @@ static HTTPContext *find_rtp_session_with_url(const char *url, |
|
|
|
|
for(s=0; s<rtp_c->stream->nb_streams; ++s) { |
|
|
|
|
snprintf(buf, sizeof(buf), "%s/streamid=%d", |
|
|
|
|
rtp_c->stream->filename, s); |
|
|
|
|
if(!strncmp(path, buf, sizeof(buf))) { |
|
|
|
|
// XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
|
|
|
|
|
if(!strncmp(path, buf, sizeof(buf))) |
|
|
|
|
/* XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE
|
|
|
|
|
* if nb_streams>1? */ |
|
|
|
|
return rtp_c; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
len = strlen(path); |
|
|
|
|
if (len > 0 && path[len - 1] == '/' && |
|
|
|
@ -3227,7 +3231,7 @@ static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr, |
|
|
|
|
const char *proto_str; |
|
|
|
|
|
|
|
|
|
/* XXX: should output a warning page when coming
|
|
|
|
|
close to the connection limit */ |
|
|
|
|
* close to the connection limit */ |
|
|
|
|
if (nb_connections >= config.nb_max_connections) |
|
|
|
|
goto fail; |
|
|
|
|
|
|
|
|
@ -3282,9 +3286,11 @@ static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr, |
|
|
|
|
return NULL; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* add a new RTP stream in an RTP connection (used in RTSP SETUP
|
|
|
|
|
command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is |
|
|
|
|
used. */ |
|
|
|
|
/**
|
|
|
|
|
* add a new RTP stream in an RTP connection (used in RTSP SETUP |
|
|
|
|
* command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is |
|
|
|
|
* used. |
|
|
|
|
*/ |
|
|
|
|
static int rtp_new_av_stream(HTTPContext *c, |
|
|
|
|
int stream_index, struct sockaddr_in *dest_addr, |
|
|
|
|
HTTPContext *rtsp_c) |
|
|
|
@ -3362,10 +3368,10 @@ static int rtp_new_av_stream(HTTPContext *c, |
|
|
|
|
|
|
|
|
|
/* normally, no packets should be output here, but the packet size may
|
|
|
|
|
* be checked */ |
|
|
|
|
if (ffio_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) { |
|
|
|
|
if (ffio_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) |
|
|
|
|
/* XXX: close stream */ |
|
|
|
|
goto fail; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (avformat_write_header(ctx, NULL) < 0) { |
|
|
|
|
fail: |
|
|
|
|
if (h) |
|
|
|
@ -3402,12 +3408,12 @@ static AVStream *add_av_stream1(FFServerStream *stream, |
|
|
|
|
return NULL; |
|
|
|
|
} |
|
|
|
|
avcodec_copy_context(fst->codec, codec); |
|
|
|
|
} else { |
|
|
|
|
} else |
|
|
|
|
/* live streams must use the actual feed's codec since it may be
|
|
|
|
|
* updated later to carry extradata needed by them. |
|
|
|
|
*/ |
|
|
|
|
fst->codec = codec; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fst->priv_data = av_mallocz(sizeof(FeedData)); |
|
|
|
|
fst->index = stream->nb_streams; |
|
|
|
|
avpriv_set_pts_info(fst, 33, 1, 90000); |
|
|
|
@ -3539,7 +3545,7 @@ static void build_file_streams(void) |
|
|
|
|
/* open stream */ |
|
|
|
|
if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) { |
|
|
|
|
/* specific case : if transport stream output to RTP,
|
|
|
|
|
we use a raw transport stream reader */ |
|
|
|
|
* we use a raw transport stream reader */ |
|
|
|
|
av_dict_set(&stream->in_opts, "mpeg2ts_compute_pcr", "1", 0); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -3561,7 +3567,7 @@ static void build_file_streams(void) |
|
|
|
|
remove_stream(stream); |
|
|
|
|
} else { |
|
|
|
|
/* find all the AVStreams inside and reference them in
|
|
|
|
|
'stream' */ |
|
|
|
|
* 'stream' */ |
|
|
|
|
if (avformat_find_stream_info(infile, NULL) < 0) { |
|
|
|
|
http_log("Could not find codec parameters from '%s'\n", |
|
|
|
|
stream->feed_filename); |
|
|
|
|