#define _XOPEN_SOURCE 600
#include "config.h"
-#ifndef HAVE_CLOSESOCKET
+#if !HAVE_CLOSESOCKET
#define closesocket close
#endif
#include <string.h>
#include <strings.h>
#include <stdlib.h>
-#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 <stdarg.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
-#ifdef HAVE_POLL_H
+#if HAVE_POLL_H
#include <poll.h>
#endif
#include <errno.h>
#include <time.h>
#include <sys/wait.h>
#include <signal.h>
-#ifdef HAVE_DLFCN_H
+#if HAVE_DLFCN_H
#include <dlfcn.h>
#endif
#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;
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];
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 */
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,
/* 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);
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;
}
}
-void __attribute__ ((format (printf, 1, 2))) http_log(const char *fmt, ...)
+static void __attribute__ ((format (printf, 1, 2))) http_log(const char *fmt, ...)
{
va_list vargs;
va_start(vargs, fmt);
{
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), ptr);
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) {
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;
return -1;
}
- http_log("ffserver started.\n");
+ http_log("FFserver started.\n");
start_children(first_feed);
}
}
+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"
+ "<html><head><title>Too busy</title></head><body>\r\n"
+ "<p>The server is too busy to serve your request at this time.</p>\r\n"
+ "<p>The number of current connections is %d, and this exceeds the limit of %d.</p>\r\n"
+ "</body></html>\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;
}
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));
*pp = p;
}
+static void get_arg(char *buf, int buf_size, const char **pp)
+{
+ const char *p;
+ char *q;
+ int quote;
+
+ p = *pp;
+ while (isspace(*p)) p++;
+ q = buf;
+ quote = 0;
+ if (*p == '\"' || *p == '\'')
+ quote = *p++;
+ for(;;) {
+ if (quote) {
+ if (*p == quote)
+ break;
+ } else {
+ if (isspace(*p))
+ break;
+ }
+ if (*p == '\0')
+ break;
+ if ((q - buf) < buf_size - 1)
+ *q++ = *p;
+ p++;
+ }
+ *q = '\0';
+ if (quote && *p == quote)
+ p++;
+ *pp = p;
+}
+
static int validate_acl(FFStream *stream, HTTPContext *c)
{
enum IPAddressAction last_action = IP_DENY;
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, '?');
}
redir_type = REDIR_NONE;
- if (match_ext(filename, "asx")) {
+ if (av_match_ext(filename, "asx")) {
redir_type = REDIR_ASX;
filename[strlen(filename)-1] = 'f';
- } else if (match_ext(filename, "asf") &&
+ } else if (av_match_ext(filename, "asf") &&
(!useragent || strncasecmp(useragent, "NSPlayer", 8) != 0)) {
/* if this isn't WMP or lookalike, return the redirector file */
redir_type = REDIR_ASF;
- } else if (match_ext(filename, "rpm,ram")) {
+ } else if (av_match_ext(filename, "rpm,ram")) {
redir_type = REDIR_RAM;
strcpy(filename + strlen(filename)-2, "m");
- } else if (match_ext(filename, "rtsp")) {
+ } else if (av_match_ext(filename, "rtsp")) {
redir_type = REDIR_RTSP;
compute_real_filename(filename, sizeof(filename) - 1);
- } else if (match_ext(filename, "sdp")) {
+ } else if (av_match_ext(filename, "sdp")) {
redir_type = REDIR_SDP;
compute_real_filename(filename, sizeof(filename) - 1);
}
}
if (stream == NULL) {
snprintf(msg, sizeof(msg), "File '%s' not found", url);
+ http_log("File '%s' not found\n", url);
goto send_error;
}
/* 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;
"\r\n"
"<html><head><title>Too busy</title></head><body>\r\n"
"<p>The server is too busy to serve your request at this time.</p>\r\n"
- "<p>The bandwidth being served (including your stream) is %lldkbit/sec, "
- "and this exceeds the limit of %lldkbit/sec.</p>\r\n"
+ "<p>The bandwidth being served (including your stream) is %"PRIu64"kbit/sec, "
+ "and this exceeds the limit of %"PRIu64"kbit/sec.</p>\r\n"
"</body></html>\r\n", current_bandwidth, max_bandwidth);
/* prepare output buffer */
c->buffer_ptr = c->buffer;
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);
}
"HTTP/1.0 404 Not Found\r\n"
"Content-type: text/html\r\n"
"\r\n"
- "<HTML>\n"
- "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n"
- "<BODY>%s</BODY>\n"
- "</HTML>\n", msg);
+ "<html>\n"
+ "<head><title>404 Not Found</title></head>\n"
+ "<body>%s</body>\n"
+ "</html>\n", msg);
/* prepare output buffer */
c->buffer_ptr = c->buffer;
c->buffer_end = q;
url_fprintf(pb, "Pragma: no-cache\r\n");
url_fprintf(pb, "\r\n");
- url_fprintf(pb, "<HTML><HEAD><TITLE>%s Status</TITLE>\n", program_name);
+ url_fprintf(pb, "<html><head><title>%s Status</title>\n", program_name);
if (c->stream->feed_filename[0])
url_fprintf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
- url_fprintf(pb, "</HEAD>\n<BODY>");
- url_fprintf(pb, "<H1>%s Status</H1>\n", program_name);
+ url_fprintf(pb, "</head>\n<body>");
+ url_fprintf(pb, "<h1>%s Status</h1>\n", program_name);
/* format status */
- 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");
+ 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");
stream = first_stream;
while (stream != NULL) {
char sfilename[1024];
}
}
- url_fprintf(pb, "<TR><TD><A HREF=\"/%s\">%s</A> ",
+ url_fprintf(pb, "<tr><td><a href=\"/%s\">%s</a> ",
sfilename, stream->filename);
url_fprintf(pb, "<td align=right> %d <td align=right> ",
stream->conns_served);
abort();
}
}
- 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",
+ 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,
stream->bandwidth,
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)
- url_fprintf(pb, "<TD>%s", stream->feed->filename);
+ url_fprintf(pb, "<td>%s", stream->feed->filename);
else
- url_fprintf(pb, "<TD>%s", stream->feed_filename);
+ url_fprintf(pb, "<td>%s", stream->feed_filename);
url_fprintf(pb, "\n");
}
break;
default:
- url_fprintf(pb, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
+ url_fprintf(pb, "<td align=center> - <td align=right> - <td align=right> - <td><td align=right> - <td>\n");
break;
}
}
stream = stream->next;
}
- url_fprintf(pb, "</TABLE>\n");
+ url_fprintf(pb, "</table>\n");
stream = first_stream;
while (stream != NULL) {
stream = stream->next;
}
-#if 0
- {
- float avg;
- AVCodecContext *enc;
- char buf[1024];
-
- /* feed status */
- stream = first_feed;
- while (stream != NULL) {
- 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;
- url_fprintf(pb, "<TR><TD>%s <TD> %d <TD> %"PRId64" <TD> %0.1f\n",
- buf, enc->frame_number, fdata->data_count, avg / 1000.0);
- }
- url_fprintf(pb, "</TABLE>\n");
- stream = stream->next_feed;
- }
- }
-#endif
-
/* connection status */
- url_fprintf(pb, "<H2>Connection Status</H2>\n");
+ url_fprintf(pb, "<h2>Connection Status</h2>\n");
- url_fprintf(pb, "Number of connections: %d / %d<BR>\n",
+ url_fprintf(pb, "Number of connections: %d / %d<br>\n",
nb_connections, nb_max_connections);
- url_fprintf(pb, "Bandwidth in use: %lldk / %lldk<BR>\n",
+ url_fprintf(pb, "Bandwidth in use: %"PRIu64"k / %"PRIu64"k<br>\n",
current_bandwidth, max_bandwidth);
- 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");
+ 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");
c1 = first_http_ctx;
i = 0;
while (c1 != NULL) {
i++;
p = inet_ntoa(c1->from_addr.sin_addr);
- url_fprintf(pb, "<TR><TD><B>%d</B><TD>%s%s<TD>%s<TD>%s<TD>%s<td align=right>",
+ 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)" : "",
url_fprintf(pb, "\n");
c1 = c1->next;
}
- url_fprintf(pb, "</TABLE>\n");
+ url_fprintf(pb, "</table>\n");
/* date */
ti = time(NULL);
p = ctime(&ti);
- url_fprintf(pb, "<HR size=1 noshade>Generated at %s", p);
- url_fprintf(pb, "</BODY>\n</HTML>\n");
+ url_fprintf(pb, "<hr size=1 noshade>Generated at %s", p);
+ url_fprintf(pb, "</body>\n</html>\n");
len = url_close_dyn_buf(pb, &c->pb_buffer);
c->buffer_ptr = c->pb_buffer;
if (input_filename[0] == '\0')
return -1;
-#if 0
- { time_t when = stream_pos / 1000000;
- http_log("Stream pos = %"PRId64", time=%s", stream_pos, ctime(&when));
- }
-#endif
-
/* open stream */
if ((ret = av_open_input_file(&s, input_filename, c->stream->ifmt,
buf_size, c->stream->ap_in)) < 0) {
}
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;i<s->nb_streams;i++)
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;i<c->stream->nb_streams;i++) {
AVStream *st;
if (ist->start_time != AV_NOPTS_VALUE)
c->cur_pts -= av_rescale_q(ist->start_time, ist->time_base, AV_TIME_BASE_Q);
c->cur_frame_duration = av_rescale_q(pkt.duration, ist->time_base, AV_TIME_BASE_Q);
-#if 0
- printf("index=%d pts=%0.3f duration=%0.6f\n",
- pkt.stream_index,
- (double)c->cur_pts /
- AV_TIME_BASE,
- (double)c->cur_frame_duration /
- AV_TIME_BASE);
-#endif
/* find RTP context */
c->packet_stream_index = pkt.stream_index;
ctx = c->rtp_ctx[c->packet_stream_index];
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]);
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;
}
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);
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) {
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;
}
char protocol[32];
char line[1024];
int len;
- RTSPHeader header1, *header = &header1;
+ RTSPMessageHeader header1, *header = &header1;
c->buffer_ptr[0] = '\0';
p = c->buffer;
}
/* parse each header line */
- memset(header, 0, sizeof(RTSPHeader));
+ memset(header, 0, sizeof(*header));
/* skip to next line */
while (*p != '\n' && *p != '\0')
p++;
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",
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;i<h->nb_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;
/* 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;
}
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;
/* 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;
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;
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;
return;
}
-#if 0
- /* XXX: seek in stream */
- if (h->range_start != AV_NOPTS_VALUE) {
- printf("range_start=%0.3f\n", (double)h->range_start / AV_TIME_BASE);
- av_seek_frame(rtp_c->fmt_in, -1, h->range_start);
- }
-#endif
-
rtp_c->state = HTTPSTATE_SEND_DATA;
/* now everything is OK, so we can send the connection parameters */
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;
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];
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;
/* 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:
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);
+ ctx->oformat = av_guess_format("rtp", NULL, NULL);
st = av_mallocz(sizeof(AVStream));
if (!st)
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 ? */
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;
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);
}
}
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",
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)
}
}
-static void get_arg(char *buf, int buf_size, const char **pp)
-{
- const char *p;
- char *q;
- int quote;
-
- p = *pp;
- while (isspace(*p)) p++;
- q = buf;
- quote = 0;
- if (*p == '\"' || *p == '\'')
- quote = *p++;
- for(;;) {
- if (quote) {
- if (*p == quote)
- break;
- } else {
- if (isspace(*p))
- break;
- }
- if (*p == '\0')
- break;
- if ((q - buf) < buf_size - 1)
- *q++ = *p;
- p++;
- }
- *q = '\0';
- if (quote && *p == quote)
- p++;
- *pp = p;
-}
-
/* add a codec and set the default parameters */
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);
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);
/* simplistic plugin support */
-#ifdef HAVE_DLOPEN
+#if HAVE_DLOPEN
static void load_module(const char *filename)
{
void *dll;
}
#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 AVOutputFormat *ffserver_guess_format(const char *short_name, const char *filename,
+ const char *mime_type)
+{
+ AVOutputFormat *fmt = av_guess_format(short_name, filename, mime_type);
+
+ if (fmt) {
+ AVOutputFormat *stream_fmt;
+ char stream_format_name[64];
+
+ snprintf(stream_format_name, sizeof(stream_format_name), "%s_stream", fmt->name);
+ stream_fmt = av_guess_format(stream_format_name, NULL, NULL);
+
+ if (stream_fmt)
+ fmt = stream_fmt;
+ }
+
+ return fmt;
}
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) {
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';
- feed->fmt = guess_format("ffm", NULL, NULL);
+
+ 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 = av_guess_format("ffm", NULL, NULL);
/* defaut feed file */
snprintf(feed->feed_filename, sizeof(feed->feed_filename),
"/tmp/%s.ffm", feed->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) {
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;
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, "</Feed>")) {
if (!feed) {
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';
- stream->fmt = guess_stream_format(NULL, stream->filename, NULL);
+
+ 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 = ffserver_guess_format(NULL, stream->filename, NULL);
/* fetch avclass so AVOption works
* FIXME try to use avcodec_get_context_defaults2
* without changing defaults too much */
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);
/* jpeg cannot be used here, so use single frame jpeg */
if (!strcmp(arg, "jpeg"))
strcpy(arg, "mjpeg");
- stream->fmt = guess_stream_format(arg, NULL, NULL);
+ stream->fmt = ffserver_guess_format(arg, NULL, NULL);
if (!stream->fmt) {
fprintf(stderr, "%s:%d: Unknown Format: %s\n",
filename, line_num, arg);
}
} 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) {
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;
}
} 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",
} else {
fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n",
filename, line_num, cmd);
- errors++;
}
}
need_to_start_children = 1;
}
-static void opt_debug()
+static void opt_debug(void)
{
ffserver_debug = 1;
ffserver_daemon = 0;
logfilename[0] = '-';
}
-static void opt_show_help(void)
+static void show_help(void)
{
printf("usage: ffserver [options]\n"
"Hyper fast multi format Audio/Video streaming server\n");
}
static const OptionDef options[] = {
- { "h", OPT_EXIT, {(void*)opt_show_help}, "show help" },
- { "version", OPT_EXIT, {(void*)show_version}, "show version" },
- { "L", OPT_EXIT, {(void*)show_license}, "show license" },
- { "formats", OPT_EXIT, {(void*)show_formats}, "show available formats, codecs, protocols, ..." },
+#include "cmdutils_common_opts.h"
{ "n", OPT_BOOL, {(void *)&no_launch }, "enable no-launch mode" },
{ "d", 0, {(void*)opt_debug}, "enable debug mode" },
{ "f", HAS_ARG | OPT_STRING, {(void*)&config_filename }, "use configfile instead of /etc/ffserver.conf", "configfile" },
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;