/*
- * Multiple format streaming server
* Copyright (c) 2000, 2001, 2002 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
+/**
+ * @file
+ * multiple format streaming server based on the FFmpeg libraries
+ */
+
#include "config.h"
#if !HAVE_CLOSESOCKET
#define closesocket close
#include <string.h>
#include <stdlib.h>
#include "libavformat/avformat.h"
-// FIXME those are internal headers, avserver _really_ shouldn't use them
+// FIXME those are internal headers, ffserver _really_ shouldn't use them
#include "libavformat/ffm.h"
#include "libavformat/network.h"
#include "libavformat/os_support.h"
#include "libavformat/internal.h"
#include "libavformat/url.h"
+#include "libavutil/avassert.h"
#include "libavutil/avstring.h"
#include "libavutil/lfg.h"
#include "libavutil/dict.h"
#include "cmdutils.h"
-const char program_name[] = "avserver";
+const char program_name[] = "ffserver";
const int program_birth_year = 2000;
static const OptionDef options[];
struct in_addr last;
} IPAddressACL;
-/* description of each stream of the avserver.conf file */
+/* description of each stream of the ffserver.conf file */
typedef struct FFStream {
enum StreamType stream_type;
char filename[1024]; /* stream filename */
char title[512];
char copyright[512];
char comment[512];
- pid_t pid; /* of avconv process */
- time_t pid_start; /* of avconv process */
+ pid_t pid; /* Of ffmpeg process */
+ time_t pid_start; /* Of ffmpeg process */
char **child_argv;
struct FFStream *next;
unsigned bandwidth; /* bandwidth, in kbits/s */
static const char *my_program_name;
static const char *my_program_dir;
-static const char *config_filename = "/etc/avserver.conf";
+static const char *config_filename = "/etc/ffserver.conf";
-static int avserver_debug;
-static int avserver_daemon;
+static int ffserver_debug;
+static int ffserver_daemon;
static int no_launch;
static int need_to_start_children;
static FILE *logfile = NULL;
-void exit_program(int ret)
+/* FIXME: make ffserver work with IPv6 */
+void av_noreturn exit_program(int ret)
{
exit(ret);
}
-/* FIXME: make avserver work with IPv6 */
/* resolve host with also IP address parsing */
static int resolve_host(struct in_addr *sin_addr, const char *hostname)
{
slash = pathname;
else
slash++;
- strcpy(slash, "avconv");
+ strcpy(slash, "ffmpeg");
http_log("Launch command line: ");
http_log("%s ", pathname);
for (i = 3; i < 256; i++)
close(i);
- if (!avserver_debug) {
+ if (!ffserver_debug) {
i = open("/dev/null", O_RDWR);
if (i != -1) {
dup2(i, 0);
}
/* This is needed to make relative pathnames work */
- chdir(my_program_dir);
+ if (chdir(my_program_dir) < 0) {
+ http_log("chdir failed\n");
+ exit(1);
+ }
signal(SIGPIPE, SIG_DFL);
default_port = 6000;
for(stream = first_stream; stream != NULL; stream = stream->next) {
if (stream->is_multicast) {
+ unsigned random0 = av_lfg_get(&random_state);
+ unsigned random1 = av_lfg_get(&random_state);
/* open the RTP connection */
snprintf(session_id, sizeof(session_id), "%08x%08x",
- av_lfg_get(&random_state), av_lfg_get(&random_state));
+ random0, random1);
/* choose a port if none given */
if (stream->multicast_port == 0) {
return -1;
}
- http_log("AVserver started.\n");
+ http_log("FFserver started.\n");
start_children(first_feed);
poll_entry->events = POLLOUT;
poll_entry++;
} else {
- /* when avserver is doing the timing, we work by
+ /* when ffserver is doing the timing, we work by
looking at which packet need to be sent every
10 ms */
delay1 = 10; /* one tick wait XXX: 10 ms assumed */
static void http_send_too_busy_reply(int fd)
{
- char buffer[300];
+ char buffer[400];
int len = snprintf(buffer, sizeof(buffer),
"HTTP/1.0 503 Server too busy\r\n"
"Content-type: text/html\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);
+ av_assert0(len < sizeof(buffer));
send(fd, buffer, len, 0);
}
av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
- if (avserver_debug)
+ if (ffserver_debug)
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 */
if (stream->stream_type == STREAM_TYPE_REDIRECT) {
c->http_error = 301;
q = c->buffer;
- q += snprintf(q, c->buffer_size,
+ snprintf(q, c->buffer_size,
"HTTP/1.0 301 Moved\r\n"
"Location: %s\r\n"
"Content-type: text/html\r\n"
"<html><head><title>Moved</title></head><body>\r\n"
"You should be <a href=\"%s\">redirected</a>.\r\n"
"</body></html>\r\n", stream->feed_filename, stream->feed_filename);
+ q += strlen(q);
/* prepare output buffer */
c->buffer_ptr = c->buffer;
c->buffer_end = q;
if (c->post == 0 && max_bandwidth < current_bandwidth) {
c->http_error = 503;
q = c->buffer;
- q += snprintf(q, c->buffer_size,
+ snprintf(q, c->buffer_size,
"HTTP/1.0 503 Server too busy\r\n"
"Content-type: text/html\r\n"
"\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);
+ q += strlen(q);
/* prepare output buffer */
c->buffer_ptr = c->buffer;
c->buffer_end = q;
q = c->buffer;
switch(redir_type) {
case REDIR_ASX:
- q += snprintf(q, c->buffer_size,
+ snprintf(q, c->buffer_size,
"HTTP/1.0 200 ASX Follows\r\n"
"Content-type: video/x-ms-asf\r\n"
"\r\n"
"<ASX Version=\"3\">\r\n"
- //"<!-- Autogenerated by avserver -->\r\n"
+ //"<!-- Autogenerated by ffserver -->\r\n"
"<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n"
"</ASX>\r\n", hostbuf, filename, info);
+ q += strlen(q);
break;
case REDIR_RAM:
- q += snprintf(q, c->buffer_size,
+ 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 avserver\r\n"
+ "# Autogenerated by ffserver\r\n"
"http://%s/%s%s\r\n", hostbuf, filename, info);
+ q += strlen(q);
break;
case REDIR_ASF:
- q += snprintf(q, c->buffer_size,
+ 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);
+ q += strlen(q);
break;
case REDIR_RTSP:
{
p = strrchr(hostname, ':');
if (p)
*p = '\0';
- q += snprintf(q, c->buffer_size,
+ 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);
+ q += strlen(q);
}
break;
case REDIR_SDP:
int sdp_data_size, len;
struct sockaddr_in my_addr;
- q += snprintf(q, c->buffer_size,
+ snprintf(q, c->buffer_size,
"HTTP/1.0 200 OK\r\n"
"Content-type: application/sdp\r\n"
"\r\n");
+ q += strlen(q);
len = sizeof(my_addr);
getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
}
/* prepare http header */
- q = c->buffer;
- q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
+ c->buffer[0] = 0;
+ av_strlcatf(c->buffer, c->buffer_size, "HTTP/1.0 200 OK\r\n");
mime_type = c->stream->fmt->mime_type;
if (!mime_type)
mime_type = "application/x-octet-stream";
- q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Pragma: no-cache\r\n");
+ av_strlcatf(c->buffer, c->buffer_size, "Pragma: no-cache\r\n");
/* for asf, we need extra headers */
if (!strcmp(c->stream->fmt->name,"asf_stream")) {
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);
+ av_strlcatf(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);
}
- q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-Type: %s\r\n", mime_type);
- q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
+ av_strlcatf(c->buffer, c->buffer_size, "Content-Type: %s\r\n", mime_type);
+ av_strlcatf(c->buffer, c->buffer_size, "\r\n");
+ q = c->buffer + strlen(c->buffer);
/* prepare output buffer */
c->http_error = 0;
send_error:
c->http_error = 404;
q = c->buffer;
- q += snprintf(q, c->buffer_size,
+ snprintf(q, c->buffer_size,
"HTTP/1.0 404 Not Found\r\n"
"Content-type: text/html\r\n"
"\r\n"
"<head><title>404 Not Found</title></head>\n"
"<body>%s</body>\n"
"</html>\n", msg);
+ q += strlen(q);
/* prepare output buffer */
c->buffer_ptr = c->buffer;
c->buffer_end = q;
char buf[128];
char input_filename[1024];
AVFormatContext *s = NULL;
- int i, ret;
+ int buf_size, i, ret;
int64_t stream_pos;
/* find file name */
if (c->stream->feed) {
strcpy(input_filename, c->stream->feed->feed_filename);
+ buf_size = FFM_PACKET_SIZE;
/* compute position (absolute time) */
if (av_find_info_tag(buf, sizeof(buf), "date", info)) {
if ((ret = av_parse_time(&stream_pos, buf, 0)) < 0)
stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
} else {
strcpy(input_filename, c->stream->feed_filename);
+ buf_size = 0;
/* compute position (relative time) */
if (av_find_info_tag(buf, sizeof(buf), "date", info)) {
if ((ret = av_parse_time(&stream_pos, buf, 1)) < 0)
http_log("could not open %s: %d\n", input_filename, ret);
return -1;
}
+
+ /* set buffer size */
+ if (buf_size > 0) ffio_set_buf_size(s->pb, buf_size);
+
s->flags |= AVFMT_FLAG_GENPTS;
c->fmt_in = s;
if (strcmp(s->iformat->name, "ffm") && avformat_find_stream_info(c->fmt_in, NULL) < 0) {
/*
* HACK to avoid mpeg ps muxer to spit many underflow errors
- * Default value from Libav
+ * Default value from FFmpeg
* Try to set it use configuration option
*/
c->fmt_ctx.max_delay = (int)(0.7*AV_TIME_BASE);
found:
/* generate session id if needed */
- if (h->session_id[0] == '\0')
+ if (h->session_id[0] == '\0') {
+ unsigned random0 = av_lfg_get(&random_state);
+ unsigned random1 = av_lfg_get(&random_state);
snprintf(h->session_id, sizeof(h->session_id), "%08x%08x",
- av_lfg_get(&random_state), av_lfg_get(&random_state));
+ random0, random1);
+ }
/* find rtp session, and create it if none found */
rtp_c = find_rtp_session(h->session_id);
}
/********************************************************************/
-/* avserver initialization */
+/* ffserver initialization */
static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec, int copy)
{
int matches = 0;
if (avformat_open_input(&s, feed->feed_filename, NULL, NULL) >= 0) {
+ /* set buffer size */
+ ffio_set_buf_size(s->pb, FFM_PACKET_SIZE);
/* Now see if it matches */
if (s->nb_streams == feed->nb_streams) {
matches = 1;
return;
}
- init_func = dlsym(dll, "avserver_module_init");
+ init_func = dlsym(dll, "ffserver_module_init");
if (!init_func) {
fprintf(stderr,
- "%s: init function 'avserver_module_init()' not found\n",
+ "%s: init function 'ffserver_module_init()' not found\n",
filename);
dlclose(dll);
}
}
#endif
-static int avserver_opt_default(const char *opt, const char *arg,
+static int ffserver_opt_default(const char *opt, const char *arg,
AVCodecContext *avctx, int type)
{
int ret = 0;
return ret;
}
-static int avserver_opt_preset(const char *arg,
+static int ffserver_opt_preset(const char *arg,
AVCodecContext *avctx, int type,
enum AVCodecID *audio_id, enum AVCodecID *video_id)
{
*video_id = opt_video_codec(tmp2);
}else if(!strcmp(tmp, "scodec")){
/* opt_subtitle_codec(tmp2); */
- }else if(avserver_opt_default(tmp, tmp2, avctx, type) < 0){
+ }else if(ffserver_opt_default(tmp, tmp2, avctx, type) < 0){
fprintf(stderr, "%s: Invalid option or argument: '%s', parsed as '%s' = '%s'\n", filename, line, tmp, tmp2);
ret = 1;
break;
return ret;
}
-static AVOutputFormat *avserver_guess_format(const char *short_name, const char *filename,
+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);
ERROR("%s:%d: Invalid host/IP address: %s\n", arg);
}
} else if (!av_strcasecmp(cmd, "NoDaemon")) {
- avserver_daemon = 0;
+ ffserver_daemon = 0;
} else if (!av_strcasecmp(cmd, "RTSPPort")) {
get_arg(arg, sizeof(arg), &p);
val = atoi(arg);
} else
max_bandwidth = llval;
} else if (!av_strcasecmp(cmd, "CustomLog")) {
- if (!avserver_debug)
+ if (!ffserver_debug)
get_arg(logfilename, sizeof(logfilename), &p);
} else if (!av_strcasecmp(cmd, "<Feed")) {
/*********************************************/
}
}
- stream->fmt = avserver_guess_format(NULL, stream->filename, NULL);
+ stream->fmt = ffserver_guess_format(NULL, stream->filename, NULL);
avcodec_get_context_defaults3(&video_enc, NULL);
avcodec_get_context_defaults3(&audio_enc, NULL);
+
audio_id = AV_CODEC_ID_NONE;
video_id = AV_CODEC_ID_NONE;
if (stream->fmt) {
/* jpeg cannot be used here, so use single frame jpeg */
if (!strcmp(arg, "jpeg"))
strcpy(arg, "mjpeg");
- stream->fmt = avserver_guess_format(arg, NULL, NULL);
+ stream->fmt = ffserver_guess_format(arg, NULL, NULL);
if (!stream->fmt) {
ERROR("Unknown Format: %s\n", arg);
}
avctx = &audio_enc;
type = AV_OPT_FLAG_AUDIO_PARAM;
}
- if (avserver_opt_default(arg, arg2, avctx, type|AV_OPT_FLAG_ENCODING_PARAM)) {
+ if (ffserver_opt_default(arg, arg2, avctx, type|AV_OPT_FLAG_ENCODING_PARAM)) {
ERROR("AVOption error: %s %s\n", arg, arg2);
}
} else if (!av_strcasecmp(cmd, "AVPresetVideo") ||
audio_enc.codec_id = audio_id;
type = AV_OPT_FLAG_AUDIO_PARAM;
}
- if (avserver_opt_preset(arg, avctx, type|AV_OPT_FLAG_ENCODING_PARAM, &audio_id, &video_id)) {
+ if (ffserver_opt_preset(arg, avctx, type|AV_OPT_FLAG_ENCODING_PARAM, &audio_id, &video_id)) {
ERROR("AVPreset error: %s\n", arg);
}
} else if (!av_strcasecmp(cmd, "VideoTag")) {
static void opt_debug(void)
{
- avserver_debug = 1;
- avserver_daemon = 0;
+ ffserver_debug = 1;
+ ffserver_daemon = 0;
logfilename[0] = '-';
}
void show_help_default(const char *opt, const char *arg)
{
- printf("usage: avserver [options]\n"
+ printf("usage: ffserver [options]\n"
"Hyper fast multi format Audio/Video streaming server\n");
printf("\n");
show_help_options(options, "Main options:", 0, 0, 0);
#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/avserver.conf", "configfile" },
+ { "f", HAS_ARG | OPT_STRING, {(void*)&config_filename }, "use configfile instead of /etc/ffserver.conf", "configfile" },
{ NULL },
};
av_register_all();
avformat_network_init();
- show_banner();
+ show_banner(argc, argv, options);
my_program_name = argv[0];
my_program_dir = getcwd(0, 0);
- avserver_daemon = 1;
+ ffserver_daemon = 1;
parse_options(NULL, argc, argv, options, NULL);
compute_bandwidth();
/* put the process in background and detach it from its TTY */
- if (avserver_daemon) {
+ if (ffserver_daemon) {
int pid;
pid = fork();
/* signal init */
signal(SIGPIPE, SIG_IGN);
- if (avserver_daemon)
+ if (ffserver_daemon)
chdir("/");
if (http_server() < 0) {