X-Git-Url: https://rtime.felk.cvut.cz/gitweb/frescor/ffmpeg.git/blobdiff_plain/af92c5150953872c81e5135c971d5acabad9b777..refs/heads/omkize:/ffserver.c diff --git a/ffserver.c b/ffserver.c index 3a854b3bf..61ecf206d 100644 --- a/ffserver.c +++ b/ffserver.c @@ -19,25 +19,31 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#define _XOPEN_SOURCE 600 + #include "config.h" -#ifndef HAVE_CLOSESOCKET +#if !HAVE_CLOSESOCKET #define closesocket close #endif #include +#include #include -#include "libavutil/random.h" -#include "libavutil/avstring.h" +/* avformat.h defines LIBAVFORMAT_BUILD, include it before all the other libav* headers which use it */ #include "libavformat/avformat.h" #include "libavformat/network.h" #include "libavformat/os_support.h" -#include "libavformat/rtp.h" +#include "libavformat/rtpdec.h" #include "libavformat/rtsp.h" +#include "libavutil/avstring.h" +#include "libavutil/lfg.h" +#include "libavutil/random_seed.h" +#include "libavutil/intreadwrite.h" #include "libavcodec/opt.h" #include #include #include #include -#ifdef HAVE_POLL_H +#if HAVE_POLL_H #include #endif #include @@ -46,7 +52,7 @@ #include #include #include -#ifdef HAVE_DLFCN_H +#if HAVE_DLFCN_H #include #endif @@ -59,9 +65,6 @@ const int program_birth_year = 2000; static const OptionDef options[]; -/* maximum number of simultaneous HTTP connections */ -#define HTTP_MAX_CONNECTIONS 2000 - enum HTTPState { HTTPSTATE_WAIT_REQUEST, HTTPSTATE_SEND_HEADER, @@ -101,6 +104,11 @@ static const char *http_state[] = { #define SYNC_TIMEOUT (10 * 1000) +typedef struct RTSPActionServerSetup { + uint32_t ipaddr; + char transport_option[512]; +} RTSPActionServerSetup; + typedef struct { int64_t count1, count2; int64_t time1, time2; @@ -157,7 +165,7 @@ typedef struct HTTPContext { int seq; /* RTSP sequence number */ /* RTP state specific */ - enum RTSPProtocol rtp_protocol; + enum RTSPLowerTransport rtp_protocol; char session_id[32]; /* session id */ AVFormatContext *rtp_ctx[MAX_STREAMS]; @@ -229,6 +237,7 @@ typedef struct FFStream { int feed_opened; /* true if someone is writing to the feed */ int is_feed; /* true if it is a feed */ int readonly; /* True if writing is prohibited to the file */ + int truncate; /* True if feeder connection truncate the feed file */ int conns_served; int64_t bytes_served; int64_t feed_max_size; /* maximum storage size, zero means unlimited */ @@ -266,10 +275,10 @@ static int http_receive_data(HTTPContext *c); static int rtsp_parse_request(HTTPContext *c); static void rtsp_cmd_describe(HTTPContext *c, const char *url); static void rtsp_cmd_options(HTTPContext *c, const char *url); -static void rtsp_cmd_setup(HTTPContext *c, const char *url, RTSPHeader *h); -static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h); -static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h); -static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h); +static void rtsp_cmd_setup(HTTPContext *c, const char *url, RTSPMessageHeader *h); +static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPMessageHeader *h); +static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPMessageHeader *h); +static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPMessageHeader *h); /* SDP handling */ static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer, @@ -278,7 +287,7 @@ static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer, /* RTP handling */ static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr, FFStream *stream, const char *session_id, - enum RTSPProtocol rtp_protocol); + enum RTSPLowerTransport rtp_protocol); static int rtp_new_av_stream(HTTPContext *c, int stream_index, struct sockaddr_in *dest_addr, HTTPContext *rtsp_c); @@ -292,15 +301,17 @@ static int ffserver_daemon; static int no_launch; static int need_to_start_children; -static int nb_max_connections = 5; -static int nb_connections; +/* maximum number of simultaneous HTTP connections */ +static unsigned int nb_max_http_connections = 2000; +static unsigned int nb_max_connections = 5; +static unsigned int nb_connections; static uint64_t max_bandwidth = 1000; static uint64_t current_bandwidth; static int64_t cur_time; // Making this global saves on passing it around everywhere -static AVRandomState random_state; +static AVLFG random_state; static FILE *logfile = NULL; @@ -345,10 +356,10 @@ static void http_av_log(void *ptr, int level, const char *fmt, va_list vargs) { static int print_prefix = 1; AVClass *avc = ptr ? *(AVClass**)ptr : NULL; - if (level > av_log_level) + if (level > av_log_get_level()) return; if (print_prefix && avc) - http_log("[%s @ %p]", avc->item_name(ptr), avc); + http_log("[%s @ %p]", avc->item_name(ptr), ptr); print_prefix = strstr(fmt, "\n") != NULL; http_vlog(fmt, vargs); } @@ -494,7 +505,7 @@ static void start_multicast(void) if (stream->is_multicast) { /* open the RTP connection */ snprintf(session_id, sizeof(session_id), "%08x%08x", - av_random(&random_state), av_random(&random_state)); + av_lfg_get(&random_state), av_lfg_get(&random_state)); /* choose a port if none given */ if (stream->multicast_port == 0) { @@ -507,7 +518,7 @@ static void start_multicast(void) dest_addr.sin_port = htons(stream->multicast_port); rtp_c = rtp_new_connection(&dest_addr, stream, session_id, - RTSP_PROTOCOL_RTP_UDP_MULTICAST); + RTSP_LOWER_TRANSPORT_UDP_MULTICAST); if (!rtp_c) continue; @@ -540,9 +551,14 @@ static int http_server(void) { int server_fd = 0, rtsp_server_fd = 0; int ret, delay, delay1; - struct pollfd poll_table[HTTP_MAX_CONNECTIONS + 2], *poll_entry; + struct pollfd *poll_table, *poll_entry; HTTPContext *c, *c_next; + if(!(poll_table = av_mallocz((nb_max_http_connections + 2)*sizeof(*poll_table)))) { + http_log("Impossible to allocate a poll table handling %d connections.\n", nb_max_http_connections); + return -1; + } + if (my_http_addr.sin_port) { server_fd = socket_open_listen(&my_http_addr); if (server_fd < 0) @@ -560,13 +576,10 @@ static int http_server(void) return -1; } - http_log("ffserver started.\n"); + http_log("FFserver started.\n"); start_children(first_feed); - first_http_ctx = NULL; - nb_connections = 0; - start_multicast(); for(;;) { @@ -688,6 +701,22 @@ static void start_wait_request(HTTPContext *c, int is_rtsp) } } +static void http_send_too_busy_reply(int fd) +{ + char buffer[300]; + int len = snprintf(buffer, sizeof(buffer), + "HTTP/1.0 200 Server too busy\r\n" + "Content-type: text/html\r\n" + "\r\n" + "Too busy\r\n" + "

The server is too busy to serve your request at this time.

\r\n" + "

The number of current connections is %d, and this exceeds the limit of %d.

\r\n" + "\r\n", + nb_connections, nb_max_connections); + send(fd, buffer, len, 0); +} + + static void new_connection(int server_fd, int is_rtsp) { struct sockaddr_in from_addr; @@ -703,10 +732,10 @@ static void new_connection(int server_fd, int is_rtsp) } ff_socket_nonblock(fd, 1); - /* XXX: should output a warning page when coming - close to the connection limit */ - if (nb_connections >= nb_max_connections) + if (nb_connections >= nb_max_connections) { + http_send_too_busy_reply(fd); goto fail; + } /* add a new connection */ c = av_mallocz(sizeof(HTTPContext)); @@ -792,7 +821,7 @@ static void close_connection(HTTPContext *c) ctx = &c->fmt_ctx; - if (!c->last_packet_sent) { + if (!c->last_packet_sent && c->state == HTTPSTATE_SEND_DATA_TRAILER) { if (ctx->oformat) { /* prepare header */ if (url_open_dyn_buf(&ctx->pb) >= 0) { @@ -1252,7 +1281,7 @@ static int http_parse_request(HTTPContext *c) av_strlcpy(c->protocol, protocol, sizeof(c->protocol)); if (ffserver_debug) - http_log("New connection: %s %s\n", cmd, url); + 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 */ p = strchr(url, '?'); @@ -1309,6 +1338,7 @@ static int http_parse_request(HTTPContext *c) } if (stream == NULL) { snprintf(msg, sizeof(msg), "File '%s' not found", url); + http_log("File '%s' not found\n", url); goto send_error; } @@ -1319,14 +1349,14 @@ static int http_parse_request(HTTPContext *c) if (stream->stream_type == STREAM_TYPE_REDIRECT) { c->http_error = 301; q = c->buffer; - q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 301 Moved\r\n"); - q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Location: %s\r\n", stream->feed_filename); - q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: text/html\r\n"); - q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n"); - q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Moved\r\n"); - q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "You should be redirected.\r\n", stream->feed_filename); - q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n"); - + 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" + "Moved\r\n" + "You should be redirected.\r\n" + "\r\n", stream->feed_filename, stream->feed_filename); /* prepare output buffer */ c->buffer_ptr = c->buffer; c->buffer_end = q; @@ -1337,35 +1367,35 @@ static int http_parse_request(HTTPContext *c) /* 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++) { + for (i = 0; i < FF_ARRAY_ELEMS(c->feed_streams); i++) { if (c->switch_feed_streams[i] >= 0) do_switch_stream(c, 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); + http_log("Feed '%s' already being received\n", stream->feed_filename); goto send_error; } - if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE) - current_bandwidth += stream->bandwidth; - if (c->post == 0 && max_bandwidth < current_bandwidth) { c->http_error = 200; q = c->buffer; - q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 Server too busy\r\n"); - q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: text/html\r\n"); - q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n"); - q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Too busy\r\n"); - q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "

The server is too busy to serve your request at this time.

\r\n"); - q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "

The bandwidth being served (including your stream) is %lldkbit/sec, and this exceeds the limit of %lldkbit/sec.

\r\n", - current_bandwidth, max_bandwidth); - q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n"); - + q += snprintf(q, c->buffer_size, + "HTTP/1.0 200 Server too busy\r\n" + "Content-type: text/html\r\n" + "\r\n" + "Too busy\r\n" + "

The server is too busy to serve your request at this time.

\r\n" + "

The bandwidth being served (including your stream) is %"PRIu64"kbit/sec, " + "and this exceeds the limit of %"PRIu64"kbit/sec.

\r\n" + "\r\n", current_bandwidth, max_bandwidth); /* prepare output buffer */ c->buffer_ptr = c->buffer; c->buffer_end = q; @@ -1408,30 +1438,30 @@ static int http_parse_request(HTTPContext *c) q = c->buffer; switch(redir_type) { case REDIR_ASX: - q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 ASX Follows\r\n"); - q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: video/x-ms-asf\r\n"); - q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n"); - q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n"); - //q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n"); - q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n", - hostbuf, filename, info); - q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n"); + q += snprintf(q, c->buffer_size, + "HTTP/1.0 200 ASX Follows\r\n" + "Content-type: video/x-ms-asf\r\n" + "\r\n" + "\r\n" + //"\r\n" + "\r\n" + "\r\n", hostbuf, filename, info); break; case REDIR_RAM: - q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 RAM Follows\r\n"); - q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: audio/x-pn-realaudio\r\n"); - q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n"); - q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "# Autogenerated by ffserver\r\n"); - q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "http://%s/%s%s\r\n", - hostbuf, filename, info); + 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); break; case REDIR_ASF: - q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 ASF Redirect follows\r\n"); - q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: video/x-ms-asf\r\n"); - q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n"); - q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "[Reference]\r\n"); - q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Ref1=http://%s/%s%s\r\n", - hostbuf, filename, info); + 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); break; case REDIR_RTSP: { @@ -1441,13 +1471,12 @@ static int http_parse_request(HTTPContext *c) p = strrchr(hostname, ':'); if (p) *p = '\0'; - q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 RTSP Redirect follows\r\n"); - /* XXX: incorrect mime type ? */ - q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: application/x-rtsp\r\n"); - q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n"); - q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "rtsp://%s:%d/%s\r\n", - hostname, ntohs(my_rtsp_addr.sin_port), - filename); + 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); } break; case REDIR_SDP: @@ -1456,9 +1485,10 @@ static int http_parse_request(HTTPContext *c) int sdp_data_size, len; struct sockaddr_in my_addr; - q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n"); - q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: application/sdp\r\n"); - q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n"); + q += snprintf(q, c->buffer_size, + "HTTP/1.0 200 OK\r\n" + "Content-type: application/sdp\r\n" + "\r\n"); len = sizeof(my_addr); getsockname(c->fd, (struct sockaddr *)&my_addr, &len); @@ -1500,9 +1530,8 @@ static int http_parse_request(HTTPContext *c) 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! Lets log the data - * as it might come in handy one day - */ + /* 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; @@ -1589,7 +1618,7 @@ static int http_parse_request(HTTPContext *c) if (!strcmp(c->stream->fmt->name,"asf_stream")) { /* Need to allocate a client id */ - c->wmp_client_id = av_random(&random_state) & 0x7fffffff; + c->wmp_client_id = av_lfg_get(&random_state); 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); } @@ -1605,14 +1634,14 @@ static int http_parse_request(HTTPContext *c) send_error: c->http_error = 404; q = c->buffer; - q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 404 Not Found\r\n"); - q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: %s\r\n", "text/html"); - q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n"); - q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\n"); - q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "404 Not Found\n"); - q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "%s\n", msg); - q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\n"); - + q += snprintf(q, c->buffer_size, + "HTTP/1.0 404 Not Found\r\n" + "Content-type: text/html\r\n" + "\r\n" + "\n" + "404 Not Found\n" + "%s\n" + "\n", msg); /* prepare output buffer */ c->buffer_ptr = c->buffer; c->buffer_end = q; @@ -1657,7 +1686,7 @@ static void compute_status(HTTPContext *c) url_fprintf(pb, "Pragma: no-cache\r\n"); url_fprintf(pb, "\r\n"); - url_fprintf(pb, "%s Status\n", program_name); + url_fprintf(pb, "%s Status\n", program_name); if (c->stream->feed_filename[0]) url_fprintf(pb, "\n", c->stream->feed_filename); url_fprintf(pb, "\n"); @@ -1857,7 +1886,7 @@ static void compute_status(HTTPContext *c) url_fprintf(pb, "Number of connections: %d / %d
\n", nb_connections, nb_max_connections); - url_fprintf(pb, "Bandwidth in use: %lldk / %lldk
\n", + url_fprintf(pb, "Bandwidth in use: %"PRIu64"k / %"PRIu64"k
\n", current_bandwidth, max_bandwidth); url_fprintf(pb, "\n"); @@ -1974,7 +2003,11 @@ static int open_input_stream(HTTPContext *c, const char *info) } s->flags |= AVFMT_FLAG_GENPTS; c->fmt_in = s; - av_find_stream_info(c->fmt_in); + if (strcmp(s->iformat->name, "ffm") && av_find_stream_info(c->fmt_in) < 0) { + http_log("Could not find stream info '%s'\n", input_filename); + av_close_input_file(s); + return -1; + } /* open each parser */ for(i=0;inb_streams;i++) @@ -2033,14 +2066,10 @@ static int http_prepare_data(HTTPContext *c) switch(c->state) { case HTTPSTATE_SEND_DATA_HEADER: memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx)); - av_strlcpy(c->fmt_ctx.author, c->stream->author, - sizeof(c->fmt_ctx.author)); - av_strlcpy(c->fmt_ctx.comment, c->stream->comment, - sizeof(c->fmt_ctx.comment)); - av_strlcpy(c->fmt_ctx.copyright, c->stream->copyright, - sizeof(c->fmt_ctx.copyright)); - av_strlcpy(c->fmt_ctx.title, c->stream->title, - sizeof(c->fmt_ctx.title)); + av_metadata_set(&c->fmt_ctx.metadata, "author" ,c->stream->author); + av_metadata_set(&c->fmt_ctx.metadata, "comment" ,c->stream->comment); + av_metadata_set(&c->fmt_ctx.metadata, "copyright",c->stream->copyright); + av_metadata_set(&c->fmt_ctx.metadata, "title" ,c->stream->title); for(i=0;istream->nb_streams;i++) { AVStream *st; @@ -2199,7 +2228,7 @@ static int http_prepare_data(HTTPContext *c) if (c->is_packetized) { int max_packet_size; - if (c->rtp_protocol == RTSP_PROTOCOL_RTP_TCP) + if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP) max_packet_size = RTSP_TCP_MAX_PACKET_SIZE; else max_packet_size = url_get_max_packet_size(c->rtp_handles[c->packet_stream_index]); @@ -2303,7 +2332,7 @@ static int http_send_data(HTTPContext *c) if (c->stream) c->stream->bytes_served += len; - if (c->rtp_protocol == RTSP_PROTOCOL_RTP_TCP) { + if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP) { /* RTP packets are sent inside the RTSP TCP connection */ ByteIOContext *pb; int interleaved_index, size; @@ -2403,7 +2432,19 @@ static int http_start_receive_data(HTTPContext *c) } c->feed_fd = fd; - c->stream->feed_write_index = ffm_read_write_index(fd); + if (c->stream->truncate) { + /* truncate feed file */ + ffm_write_write_index(c->feed_fd, FFM_PACKET_SIZE); + ftruncate(c->feed_fd, FFM_PACKET_SIZE); + http_log("Truncating feed file '%s'\n", c->stream->feed_filename); + } else { + if ((c->stream->feed_write_index = ffm_read_write_index(fd)) < 0) { + http_log("Error reading write index from feed file: %s\n", strerror(errno)); + return -1; + } + } + + c->stream->feed_write_index = FFMAX(ffm_read_write_index(fd), FFM_PACKET_SIZE); c->stream->feed_size = lseek(fd, 0, SEEK_END); lseek(fd, 0, SEEK_SET); @@ -2469,7 +2510,10 @@ static int http_receive_data(HTTPContext *c) feed->feed_write_index = FFM_PACKET_SIZE; /* write index */ - ffm_write_write_index(c->feed_fd, feed->feed_write_index); + if (ffm_write_write_index(c->feed_fd, feed->feed_write_index) < 0) { + http_log("Error writing index to feed file: %s\n", strerror(errno)); + goto fail; + } /* wake up any waiting connections */ for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) { @@ -2501,6 +2545,8 @@ static int http_receive_data(HTTPContext *c) if (s->nb_streams != feed->nb_streams) { av_close_input_stream(s); av_free(pb); + http_log("Feed '%s' stream number does not match registered feed\n", + c->stream->feed_filename); goto fail; } @@ -2612,7 +2658,7 @@ static int rtsp_parse_request(HTTPContext *c) char protocol[32]; char line[1024]; int len; - RTSPHeader header1, *header = &header1; + RTSPMessageHeader header1, *header = &header1; c->buffer_ptr[0] = '\0'; p = c->buffer; @@ -2638,7 +2684,7 @@ static int rtsp_parse_request(HTTPContext *c) } /* parse each header line */ - memset(header, 0, sizeof(RTSPHeader)); + memset(header, 0, sizeof(*header)); /* skip to next line */ while (*p != '\n' && *p != '\0') p++; @@ -2701,15 +2747,12 @@ static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer, AVStream avs[MAX_STREAMS]; int i; - avc = av_alloc_format_context(); + avc = avformat_alloc_context(); if (avc == NULL) { return -1; } - if (stream->title[0] != 0) { - av_strlcpy(avc->title, stream->title, sizeof(avc->title)); - } else { - av_strlcpy(avc->title, "No Title", sizeof(avc->title)); - } + av_metadata_set(&avc->metadata, "title", + stream->title[0] ? stream->title : "No Title"); avc->nb_streams = stream->nb_streams; if (stream->is_multicast) { snprintf(avc->filename, 1024, "rtp://%s:%d?multicast=1?ttl=%d", @@ -2795,21 +2838,21 @@ static HTTPContext *find_rtp_session(const char *session_id) return NULL; } -static RTSPTransportField *find_transport(RTSPHeader *h, enum RTSPProtocol protocol) +static RTSPTransportField *find_transport(RTSPMessageHeader *h, enum RTSPLowerTransport lower_transport) { RTSPTransportField *th; int i; for(i=0;inb_transports;i++) { th = &h->transports[i]; - if (th->protocol == protocol) + if (th->lower_transport == lower_transport) return th; } return NULL; } static void rtsp_cmd_setup(HTTPContext *c, const char *url, - RTSPHeader *h) + RTSPMessageHeader *h) { FFStream *stream; int stream_index, port; @@ -2858,15 +2901,15 @@ static void rtsp_cmd_setup(HTTPContext *c, const char *url, /* generate session id if needed */ if (h->session_id[0] == '\0') snprintf(h->session_id, sizeof(h->session_id), "%08x%08x", - av_random(&random_state), av_random(&random_state)); + av_lfg_get(&random_state), av_lfg_get(&random_state)); /* find rtp session, and create it if none found */ rtp_c = find_rtp_session(h->session_id); if (!rtp_c) { /* always prefer UDP */ - th = find_transport(h, RTSP_PROTOCOL_RTP_UDP); + th = find_transport(h, RTSP_LOWER_TRANSPORT_UDP); if (!th) { - th = find_transport(h, RTSP_PROTOCOL_RTP_TCP); + th = find_transport(h, RTSP_LOWER_TRANSPORT_TCP); if (!th) { rtsp_reply_error(c, RTSP_STATUS_TRANSPORT); return; @@ -2874,7 +2917,7 @@ static void rtsp_cmd_setup(HTTPContext *c, const char *url, } rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id, - th->protocol); + th->lower_transport); if (!rtp_c) { rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH); return; @@ -2902,7 +2945,7 @@ static void rtsp_cmd_setup(HTTPContext *c, const char *url, /* check transport */ th = find_transport(h, rtp_c->rtp_protocol); - if (!th || (th->protocol == RTSP_PROTOCOL_RTP_UDP && + if (!th || (th->lower_transport == RTSP_LOWER_TRANSPORT_UDP && th->client_port_min <= 0)) { rtsp_reply_error(c, RTSP_STATUS_TRANSPORT); return; @@ -2925,14 +2968,14 @@ static void rtsp_cmd_setup(HTTPContext *c, const char *url, url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id); switch(rtp_c->rtp_protocol) { - case RTSP_PROTOCOL_RTP_UDP: + case RTSP_LOWER_TRANSPORT_UDP: port = rtp_get_local_port(rtp_c->rtp_handles[stream_index]); url_fprintf(c->pb, "Transport: RTP/AVP/UDP;unicast;" "client_port=%d-%d;server_port=%d-%d", th->client_port_min, th->client_port_min + 1, port, port + 1); break; - case RTSP_PROTOCOL_RTP_TCP: + case RTSP_LOWER_TRANSPORT_TCP: url_fprintf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d", stream_index * 2, stream_index * 2 + 1); break; @@ -2980,7 +3023,7 @@ static HTTPContext *find_rtp_session_with_url(const char *url, return NULL; } -static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h) +static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPMessageHeader *h) { HTTPContext *rtp_c; @@ -3014,7 +3057,7 @@ static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h) url_fprintf(c->pb, "\r\n"); } -static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h) +static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPMessageHeader *h) { HTTPContext *rtp_c; @@ -3039,7 +3082,7 @@ static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h) url_fprintf(c->pb, "\r\n"); } -static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h) +static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPMessageHeader *h) { HTTPContext *rtp_c; char session_id[32]; @@ -3068,7 +3111,7 @@ static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h) static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr, FFStream *stream, const char *session_id, - enum RTSPProtocol rtp_protocol) + enum RTSPLowerTransport rtp_protocol) { HTTPContext *c = NULL; const char *proto_str; @@ -3099,13 +3142,13 @@ static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr, /* protocol is shown in statistics */ switch(c->rtp_protocol) { - case RTSP_PROTOCOL_RTP_UDP_MULTICAST: + case RTSP_LOWER_TRANSPORT_UDP_MULTICAST: proto_str = "MCAST"; break; - case RTSP_PROTOCOL_RTP_UDP: + case RTSP_LOWER_TRANSPORT_UDP: proto_str = "UDP"; break; - case RTSP_PROTOCOL_RTP_TCP: + case RTSP_LOWER_TRANSPORT_TCP: proto_str = "TCP"; break; default: @@ -3144,7 +3187,7 @@ static int rtp_new_av_stream(HTTPContext *c, int max_packet_size; /* now we can open the relevant output stream */ - ctx = av_alloc_format_context(); + ctx = avformat_alloc_context(); if (!ctx) return -1; ctx->oformat = guess_format("rtp", NULL, NULL); @@ -3169,8 +3212,8 @@ static int rtp_new_av_stream(HTTPContext *c, ipaddr = inet_ntoa(dest_addr->sin_addr); switch(c->rtp_protocol) { - case RTSP_PROTOCOL_RTP_UDP: - case RTSP_PROTOCOL_RTP_UDP_MULTICAST: + case RTSP_LOWER_TRANSPORT_UDP: + case RTSP_LOWER_TRANSPORT_UDP_MULTICAST: /* RTP/UDP case */ /* XXX: also pass as parameter to function ? */ @@ -3192,7 +3235,7 @@ static int rtp_new_av_stream(HTTPContext *c, c->rtp_handles[stream_index] = h; max_packet_size = url_get_max_packet_size(h); break; - case RTSP_PROTOCOL_RTP_TCP: + case RTSP_LOWER_TRANSPORT_TCP: /* RTP/TCP case */ c->rtsp_c = rtsp_c; max_packet_size = RTSP_TCP_MAX_PACKET_SIZE; @@ -3371,9 +3414,10 @@ static void build_file_streams(void) stream->ap_in->mpeg2ts_compute_pcr = 1; } + http_log("Opening file '%s'\n", stream->feed_filename); if ((ret = av_open_input_file(&infile, stream->feed_filename, stream->ifmt, 0, stream->ap_in)) < 0) { - http_log("could not open %s: %d\n", stream->feed_filename, ret); + http_log("Could not open '%s': %d\n", stream->feed_filename, ret); /* remove stream (no need to spend more time on it) */ fail: remove_stream(stream); @@ -3446,7 +3490,7 @@ static void build_feed_streams(void) if (sf->index != ss->index || sf->id != ss->id) { - printf("Index & Id do not match for stream %d (%s)\n", + http_log("Index & Id do not match for stream %d (%s)\n", i, feed->feed_filename); matches = 0; } else { @@ -3457,28 +3501,28 @@ static void build_feed_streams(void) #define CHECK_CODEC(x) (ccf->x != ccs->x) if (CHECK_CODEC(codec) || CHECK_CODEC(codec_type)) { - printf("Codecs do not match for stream %d\n", i); + http_log("Codecs do not match for stream %d\n", i); matches = 0; } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) { - printf("Codec bitrates do not match for stream %d\n", i); + http_log("Codec bitrates do not match for stream %d\n", i); matches = 0; } else if (ccf->codec_type == CODEC_TYPE_VIDEO) { if (CHECK_CODEC(time_base.den) || CHECK_CODEC(time_base.num) || CHECK_CODEC(width) || CHECK_CODEC(height)) { - printf("Codec width, height and framerate do not match for stream %d\n", i); + http_log("Codec width, height and framerate do not match for stream %d\n", i); matches = 0; } } else if (ccf->codec_type == CODEC_TYPE_AUDIO) { if (CHECK_CODEC(sample_rate) || CHECK_CODEC(channels) || CHECK_CODEC(frame_size)) { - printf("Codec sample_rate, channels, frame_size do not match for stream %d\n", i); + http_log("Codec sample_rate, channels, frame_size do not match for stream %d\n", i); matches = 0; } } else { - printf("Unknown codec type\n"); + http_log("Unknown codec type\n"); matches = 0; } } @@ -3486,17 +3530,17 @@ static void build_feed_streams(void) break; } } else - printf("Deleting feed file '%s' as stream counts differ (%d != %d)\n", + http_log("Deleting feed file '%s' as stream counts differ (%d != %d)\n", feed->feed_filename, s->nb_streams, feed->nb_streams); av_close_input_file(s); } else - printf("Deleting feed file '%s' as it appears to be corrupt\n", + http_log("Deleting feed file '%s' as it appears to be corrupt\n", feed->feed_filename); if (!matches) { if (feed->readonly) { - printf("Unable to delete feed file '%s' as it is marked readonly\n", + http_log("Unable to delete feed file '%s' as it is marked readonly\n", feed->feed_filename); exit(1); } @@ -3504,10 +3548,10 @@ static void build_feed_streams(void) } } if (!url_exist(feed->feed_filename)) { - AVFormatContext s1, *s = &s1; + AVFormatContext s1 = {0}, *s = &s1; if (feed->readonly) { - printf("Unable to create feed file '%s' as it is marked readonly\n", + http_log("Unable to create feed file '%s' as it is marked readonly\n", feed->feed_filename); exit(1); } @@ -3542,7 +3586,7 @@ static void build_feed_streams(void) exit(1); } - feed->feed_write_index = ffm_read_write_index(fd); + feed->feed_write_index = FFMAX(ffm_read_write_index(fd), FFM_PACKET_SIZE); feed->feed_size = lseek(fd, 0, SEEK_END); /* ensure that we do not wrap before the end of file */ if (feed->feed_max_size && feed->feed_max_size < feed->feed_size) @@ -3683,7 +3727,7 @@ static void add_codec(FFStream *stream, AVCodecContext *av) memcpy(st->codec, av, sizeof(AVCodecContext)); } -static int opt_audio_codec(const char *arg) +static enum CodecID opt_audio_codec(const char *arg) { AVCodec *p= avcodec_find_encoder_by_name(arg); @@ -3693,7 +3737,7 @@ static int opt_audio_codec(const char *arg) return p->id; } -static int opt_video_codec(const char *arg) +static enum CodecID opt_video_codec(const char *arg) { AVCodec *p= avcodec_find_encoder_by_name(arg); @@ -3705,7 +3749,7 @@ static int opt_video_codec(const char *arg) /* simplistic plugin support */ -#ifdef HAVE_DLOPEN +#if HAVE_DLOPEN static void load_module(const char *filename) { void *dll; @@ -3729,16 +3773,14 @@ static void load_module(const char *filename) } #endif -static int opt_default(const char *opt, const char *arg, +static int ffserver_opt_default(const char *opt, const char *arg, AVCodecContext *avctx, int type) { - const AVOption *o = NULL; - const AVOption *o2 = av_find_opt(avctx, opt, NULL, type, type); - if(o2) - o = av_set_string(avctx, opt, arg); - if(!o) - return -1; - return 0; + int ret = 0; + const AVOption *o = av_find_opt(avctx, opt, NULL, type, type); + if(o) + ret = av_set_string3(avctx, opt, arg, 1, NULL); + return ret; } static int parse_ffconfig(const char *filename) @@ -3750,9 +3792,9 @@ static int parse_ffconfig(const char *filename) const char *p; int val, errors, line_num; FFStream **last_stream, *stream, *redirect; - FFStream **last_feed, *feed; + FFStream **last_feed, *feed, *s; AVCodecContext audio_enc, video_enc; - int audio_id, video_id; + enum CodecID audio_id, video_id; f = fopen(filename, "r"); if (!f) { @@ -3817,10 +3859,19 @@ static int parse_ffconfig(const char *filename) filename, line_num, arg); errors++; } + } else if (!strcasecmp(cmd, "MaxHTTPConnections")) { + get_arg(arg, sizeof(arg), &p); + val = atoi(arg); + if (val < 1 || val > 65536) { + fprintf(stderr, "%s:%d: Invalid MaxHTTPConnections: %s\n", + filename, line_num, arg); + errors++; + } + nb_max_http_connections = val; } else if (!strcasecmp(cmd, "MaxClients")) { get_arg(arg, sizeof(arg), &p); val = atoi(arg); - if (val < 1 || val > HTTP_MAX_CONNECTIONS) { + if (val < 1 || val > nb_max_http_connections) { fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n", filename, line_num, arg); errors++; @@ -3849,17 +3900,19 @@ static int parse_ffconfig(const char *filename) filename, line_num); } else { feed = av_mallocz(sizeof(FFStream)); - /* add in stream list */ - *last_stream = feed; - last_stream = &feed->next; - /* add in feed list */ - *last_feed = feed; - last_feed = &feed->next_feed; - get_arg(feed->filename, sizeof(feed->filename), &p); q = strrchr(feed->filename, '>'); if (*q) *q = '\0'; + + for (s = first_feed; s; s = s->next) { + if (!strcmp(feed->filename, s->filename)) { + fprintf(stderr, "%s:%d: Feed '%s' already registered\n", + filename, line_num, s->filename); + errors++; + } + } + feed->fmt = guess_format("ffm", NULL, NULL); /* defaut feed file */ snprintf(feed->feed_filename, sizeof(feed->feed_filename), @@ -3867,6 +3920,13 @@ static int parse_ffconfig(const char *filename) feed->feed_max_size = 5 * 1024 * 1024; feed->is_feed = 1; feed->feed = feed; /* self feeding :-) */ + + /* add in stream list */ + *last_stream = feed; + last_stream = &feed->next; + /* add in feed list */ + *last_feed = feed; + last_feed = &feed->next_feed; } } else if (!strcasecmp(cmd, "Launch")) { if (feed) { @@ -3902,6 +3962,11 @@ static int parse_ffconfig(const char *filename) get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p); } else if (stream) get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p); + } else if (!strcasecmp(cmd, "Truncate")) { + if (feed) { + get_arg(arg, sizeof(arg), &p); + feed->truncate = strtod(arg, NULL); + } } else if (!strcasecmp(cmd, "FileMaxSize")) { if (feed) { char *p1; @@ -3922,6 +3987,11 @@ static int parse_ffconfig(const char *filename) break; } feed->feed_max_size = (int64_t)fsize; + if (feed->feed_max_size < FFM_PACKET_SIZE*4) { + fprintf(stderr, "%s:%d: Feed max file size is too small, " + "must be at least %d\n", filename, line_num, FFM_PACKET_SIZE*4); + errors++; + } } } else if (!strcasecmp(cmd, "")) { if (!feed) { @@ -3938,15 +4008,22 @@ static int parse_ffconfig(const char *filename) fprintf(stderr, "%s:%d: Already in a tag\n", filename, line_num); } else { + FFStream *s; const AVClass *class; stream = av_mallocz(sizeof(FFStream)); - *last_stream = stream; - last_stream = &stream->next; - get_arg(stream->filename, sizeof(stream->filename), &p); q = strrchr(stream->filename, '>'); if (*q) *q = '\0'; + + for (s = first_stream; s; s = s->next) { + if (!strcmp(stream->filename, s->filename)) { + fprintf(stderr, "%s:%d: Stream '%s' already registered\n", + filename, line_num, s->filename); + errors++; + } + } + stream->fmt = guess_stream_format(NULL, stream->filename, NULL); /* fetch avclass so AVOption works * FIXME try to use avcodec_get_context_defaults2 @@ -3963,6 +4040,9 @@ static int parse_ffconfig(const char *filename) audio_id = stream->fmt->audio_codec; video_id = stream->fmt->video_codec; } + + *last_stream = stream; + last_stream = &stream->next; } } else if (!strcasecmp(cmd, "Feed")) { get_arg(arg, sizeof(arg), &p); @@ -4006,10 +4086,12 @@ static int parse_ffconfig(const char *filename) } } else if (!strcasecmp(cmd, "InputFormat")) { get_arg(arg, sizeof(arg), &p); - stream->ifmt = av_find_input_format(arg); - if (!stream->ifmt) { - fprintf(stderr, "%s:%d: Unknown input format: %s\n", - filename, line_num, arg); + if (stream) { + stream->ifmt = av_find_input_format(arg); + if (!stream->ifmt) { + fprintf(stderr, "%s:%d: Unknown input format: %s\n", + filename, line_num, arg); + } } } else if (!strcasecmp(cmd, "FaviconURL")) { if (stream && stream->stream_type == STREAM_TYPE_STATUS) { @@ -4167,14 +4249,14 @@ static int parse_ffconfig(const char *filename) avctx = &audio_enc; type = AV_OPT_FLAG_AUDIO_PARAM; } - if (opt_default(arg, arg2, avctx, type|AV_OPT_FLAG_ENCODING_PARAM)) { + if (ffserver_opt_default(arg, arg2, avctx, type|AV_OPT_FLAG_ENCODING_PARAM)) { fprintf(stderr, "AVOption error: %s %s\n", arg, arg2); errors++; } } else if (!strcasecmp(cmd, "VideoTag")) { get_arg(arg, sizeof(arg), &p); if ((strlen(arg) == 4) && stream) - video_enc.codec_tag = ff_get_fourcc(arg); + video_enc.codec_tag = AV_RL32(arg); } else if (!strcasecmp(cmd, "BitExact")) { if (stream) video_enc.flags |= CODEC_FLAG_BITEXACT; @@ -4381,7 +4463,7 @@ static int parse_ffconfig(const char *filename) } } else if (!strcasecmp(cmd, "LoadModule")) { get_arg(arg, sizeof(arg), &p); -#ifdef HAVE_DLOPEN +#if HAVE_DLOPEN load_module(arg); #else fprintf(stderr, "%s:%d: Module support not compiled into this version: '%s'\n", @@ -4391,7 +4473,6 @@ static int parse_ffconfig(const char *filename) } else { fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n", filename, line_num, cmd); - errors++; } } @@ -4427,7 +4508,7 @@ static void handle_child_exit(int sig) need_to_start_children = 1; } -static void opt_debug() +static void opt_debug(void) { ffserver_debug = 1; ffserver_daemon = 0; @@ -4471,7 +4552,7 @@ int main(int argc, char **argv) unsetenv("http_proxy"); /* Kill the http_proxy */ - av_init_random(av_gettime() + (getpid() << 16), &random_state); + av_lfg_init(&random_state, ff_random_get_seed()); memset(&sigact, 0, sizeof(sigact)); sigact.sa_handler = handle_child_exit; @@ -4483,6 +4564,15 @@ int main(int argc, char **argv) exit(1); } + /* open log file if needed */ + if (logfilename[0] != '\0') { + if (!strcmp(logfilename, "-")) + logfile = stdout; + else + logfile = fopen(logfilename, "a"); + av_log_set_callback(http_av_log); + } + build_file_streams(); build_feed_streams(); @@ -4517,15 +4607,6 @@ int main(int argc, char **argv) /* signal init */ signal(SIGPIPE, SIG_IGN); - /* open log file if needed */ - if (logfilename[0] != '\0') { - if (!strcmp(logfilename, "-")) - logfile = stderr; - else - logfile = fopen(logfilename, "a"); - av_log_set_callback(http_av_log); - } - if (ffserver_daemon) chdir("/");