X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=ffserver.c;h=279ffaa43fc7cd900f03a5b69313b6dd33e114af;hb=6f348086914992ca739eb57d2233593ef766218d;hp=bf4c2edd04dc60d7440f71434e805c61f5f61a93;hpb=7ac884c5914cfc426f9963ff0e02d81574a09140;p=ffmpeg diff --git a/ffserver.c b/ffserver.c index bf4c2edd04d..279ffaa43fc 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]; @@ -266,10 +274,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 +286,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 +300,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 +355,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 +504,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 +517,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 +550,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,7 +575,7 @@ static int http_server(void) return -1; } - http_log("ffserver started.\n"); + http_log("FFserver started.\n"); start_children(first_feed); @@ -685,6 +700,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; @@ -700,10 +731,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)); @@ -789,7 +820,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) { @@ -1249,7 +1280,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, '?'); @@ -1306,6 +1337,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; } @@ -1316,14 +1348,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; @@ -1334,35 +1366,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; @@ -1405,30 +1437,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: { @@ -1438,13 +1470,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: @@ -1453,9 +1484,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); @@ -1497,9 +1529,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; @@ -1586,7 +1617,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); } @@ -1602,14 +1633,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; @@ -1654,7 +1685,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"); @@ -1854,7 +1885,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"); @@ -1971,7 +2002,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++) @@ -2030,14 +2065,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; @@ -2196,7 +2227,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]); @@ -2300,7 +2331,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; @@ -2400,7 +2431,12 @@ 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->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); @@ -2466,7 +2502,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) { @@ -2498,6 +2537,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; } @@ -2609,7 +2650,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; @@ -2635,7 +2676,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++; @@ -2698,15 +2739,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", @@ -2792,21 +2830,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; @@ -2855,15 +2893,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; @@ -2871,7 +2909,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; @@ -2899,7 +2937,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; @@ -2922,14 +2960,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; @@ -2977,7 +3015,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; @@ -3011,7 +3049,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; @@ -3036,7 +3074,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]; @@ -3065,7 +3103,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; @@ -3096,13 +3134,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: @@ -3141,7 +3179,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); @@ -3166,8 +3204,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 ? */ @@ -3189,7 +3227,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; @@ -3368,9 +3406,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); @@ -3501,7 +3540,7 @@ static void build_feed_streams(void) } } if (!url_exist(feed->feed_filename)) { - AVFormatContext s1, *s = &s1; + AVFormatContext s1 = {0}, *s = &s1; if (feed->readonly) { http_log("Unable to create feed file '%s' as it is marked readonly\n", @@ -3539,7 +3578,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) @@ -3680,7 +3719,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); @@ -3690,7 +3729,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); @@ -3702,7 +3741,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; @@ -3726,16 +3765,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_string2(avctx, opt, arg, 1); - 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) @@ -3747,9 +3784,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) { @@ -3814,10 +3851,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++; @@ -3846,17 +3892,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), @@ -3864,6 +3912,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) { @@ -3935,15 +3990,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 @@ -3960,6 +4022,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); @@ -4003,10 +4068,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) { @@ -4164,14 +4231,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; @@ -4378,7 +4445,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", @@ -4388,7 +4455,6 @@ static int parse_ffconfig(const char *filename) } else { fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n", filename, line_num, cmd); - errors++; } } @@ -4424,7 +4490,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; @@ -4468,7 +4534,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;