📄 ffserver.c
字号:
}
if (!feed->pid) {
/* In child */
char pathname[1024];
char *slash;
int i;
for (i = 3; i < 256; i++)
close(i);
if (!ffserver_debug) {
i = open("/dev/null", O_RDWR);
if (i)
dup2(i, 0);
dup2(i, 1);
dup2(i, 2);
if (i)
close(i);
}
av_strlcpy(pathname, my_program_name, sizeof(pathname));
slash = strrchr(pathname, '/');
if (!slash)
slash = pathname;
else
slash++;
strcpy(slash, "ffmpeg");
/* This is needed to make relative pathnames work */
chdir(my_program_dir);
signal(SIGPIPE, SIG_DFL);
execvp(pathname, feed->child_argv);
_exit(1);
}
}
}
}
/* open a listening socket */
static int socket_open_listen(struct sockaddr_in *my_addr)
{
int server_fd, tmp;
server_fd = socket(AF_INET,SOCK_STREAM,0);
if (server_fd < 0) {
perror ("socket");
return -1;
}
tmp = 1;
setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
char bindmsg[32];
snprintf(bindmsg, sizeof(bindmsg), "bind(port %d)", ntohs(my_addr->sin_port));
perror (bindmsg);
closesocket(server_fd);
return -1;
}
if (listen (server_fd, 5) < 0) {
perror ("listen");
closesocket(server_fd);
return -1;
}
ff_socket_nonblock(server_fd, 1);
return server_fd;
}
/* start all multicast streams */
static void start_multicast(void)
{
FFStream *stream;
char session_id[32];
HTTPContext *rtp_c;
struct sockaddr_in dest_addr;
int default_port, stream_index;
default_port = 6000;
for(stream = first_stream; stream != NULL; stream = stream->next) {
if (stream->is_multicast) {
/* open the RTP connection */
snprintf(session_id, sizeof(session_id), "%08x%08x",
av_random(&random_state), av_random(&random_state));
/* choose a port if none given */
if (stream->multicast_port == 0) {
stream->multicast_port = default_port;
default_port += 100;
}
dest_addr.sin_family = AF_INET;
dest_addr.sin_addr = stream->multicast_ip;
dest_addr.sin_port = htons(stream->multicast_port);
rtp_c = rtp_new_connection(&dest_addr, stream, session_id,
RTSP_PROTOCOL_RTP_UDP_MULTICAST);
if (!rtp_c)
continue;
if (open_input_stream(rtp_c, "") < 0) {
fprintf(stderr, "Could not open input stream for stream '%s'\n",
stream->filename);
continue;
}
/* open each RTP stream */
for(stream_index = 0; stream_index < stream->nb_streams;
stream_index++) {
dest_addr.sin_port = htons(stream->multicast_port +
2 * stream_index);
if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, NULL) < 0) {
fprintf(stderr, "Could not open output stream '%s/streamid=%d'\n",
stream->filename, stream_index);
exit(1);
}
}
/* change state to send data */
rtp_c->state = HTTPSTATE_SEND_DATA;
}
}
}
/* main loop of the http server */
static int http_server(void)
{
int server_fd, ret, rtsp_server_fd, delay, delay1;
struct pollfd poll_table[HTTP_MAX_CONNECTIONS + 2], *poll_entry;
HTTPContext *c, *c_next;
server_fd = socket_open_listen(&my_http_addr);
if (server_fd < 0)
return -1;
rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
if (rtsp_server_fd < 0)
return -1;
http_log("ffserver started.\n");
start_children(first_feed);
first_http_ctx = NULL;
nb_connections = 0;
start_multicast();
for(;;) {
poll_entry = poll_table;
poll_entry->fd = server_fd;
poll_entry->events = POLLIN;
poll_entry++;
poll_entry->fd = rtsp_server_fd;
poll_entry->events = POLLIN;
poll_entry++;
/* wait for events on each HTTP handle */
c = first_http_ctx;
delay = 1000;
while (c != NULL) {
int fd;
fd = c->fd;
switch(c->state) {
case HTTPSTATE_SEND_HEADER:
case RTSPSTATE_SEND_REPLY:
case RTSPSTATE_SEND_PACKET:
c->poll_entry = poll_entry;
poll_entry->fd = fd;
poll_entry->events = POLLOUT;
poll_entry++;
break;
case HTTPSTATE_SEND_DATA_HEADER:
case HTTPSTATE_SEND_DATA:
case HTTPSTATE_SEND_DATA_TRAILER:
if (!c->is_packetized) {
/* for TCP, we output as much as we can (may need to put a limit) */
c->poll_entry = poll_entry;
poll_entry->fd = fd;
poll_entry->events = POLLOUT;
poll_entry++;
} else {
/* 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 */
if (delay1 < delay)
delay = delay1;
}
break;
case HTTPSTATE_WAIT_REQUEST:
case HTTPSTATE_RECEIVE_DATA:
case HTTPSTATE_WAIT_FEED:
case RTSPSTATE_WAIT_REQUEST:
/* need to catch errors */
c->poll_entry = poll_entry;
poll_entry->fd = fd;
poll_entry->events = POLLIN;/* Maybe this will work */
poll_entry++;
break;
default:
c->poll_entry = NULL;
break;
}
c = c->next;
}
/* wait for an event on one connection. We poll at least every
second to handle timeouts */
do {
ret = poll(poll_table, poll_entry - poll_table, delay);
if (ret < 0 && ff_neterrno() != FF_NETERROR(EAGAIN) &&
ff_neterrno() != FF_NETERROR(EINTR))
return -1;
} while (ret < 0);
cur_time = av_gettime() / 1000;
if (need_to_start_children) {
need_to_start_children = 0;
start_children(first_feed);
}
/* now handle the events */
for(c = first_http_ctx; c != NULL; c = c_next) {
c_next = c->next;
if (handle_connection(c) < 0) {
/* close and free the connection */
log_connection(c);
close_connection(c);
}
}
poll_entry = poll_table;
/* new HTTP connection request ? */
if (poll_entry->revents & POLLIN)
new_connection(server_fd, 0);
poll_entry++;
/* new RTSP connection request ? */
if (poll_entry->revents & POLLIN)
new_connection(rtsp_server_fd, 1);
}
}
/* start waiting for a new HTTP/RTSP request */
static void start_wait_request(HTTPContext *c, int is_rtsp)
{
c->buffer_ptr = c->buffer;
c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */
if (is_rtsp) {
c->timeout = cur_time + RTSP_REQUEST_TIMEOUT;
c->state = RTSPSTATE_WAIT_REQUEST;
} else {
c->timeout = cur_time + HTTP_REQUEST_TIMEOUT;
c->state = HTTPSTATE_WAIT_REQUEST;
}
}
static void new_connection(int server_fd, int is_rtsp)
{
struct sockaddr_in from_addr;
int fd, len;
HTTPContext *c = NULL;
len = sizeof(from_addr);
fd = accept(server_fd, (struct sockaddr *)&from_addr,
&len);
if (fd < 0)
return;
ff_socket_nonblock(fd, 1);
/* XXX: should output a warning page when coming
close to the connection limit */
if (nb_connections >= nb_max_connections)
goto fail;
/* add a new connection */
c = av_mallocz(sizeof(HTTPContext));
if (!c)
goto fail;
c->fd = fd;
c->poll_entry = NULL;
c->from_addr = from_addr;
c->buffer_size = IOBUFFER_INIT_SIZE;
c->buffer = av_malloc(c->buffer_size);
if (!c->buffer)
goto fail;
c->next = first_http_ctx;
first_http_ctx = c;
nb_connections++;
start_wait_request(c, is_rtsp);
return;
fail:
if (c) {
av_free(c->buffer);
av_free(c);
}
closesocket(fd);
}
static void close_connection(HTTPContext *c)
{
HTTPContext **cp, *c1;
int i, nb_streams;
AVFormatContext *ctx;
URLContext *h;
AVStream *st;
/* remove connection from list */
cp = &first_http_ctx;
while ((*cp) != NULL) {
c1 = *cp;
if (c1 == c)
*cp = c->next;
else
cp = &c1->next;
}
/* remove references, if any (XXX: do it faster) */
for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
if (c1->rtsp_c == c)
c1->rtsp_c = NULL;
}
/* remove connection associated resources */
if (c->fd >= 0)
closesocket(c->fd);
if (c->fmt_in) {
/* close each frame parser */
for(i=0;i<c->fmt_in->nb_streams;i++) {
st = c->fmt_in->streams[i];
if (st->codec->codec)
avcodec_close(st->codec);
}
av_close_input_file(c->fmt_in);
}
/* free RTP output streams if any */
nb_streams = 0;
if (c->stream)
nb_streams = c->stream->nb_streams;
for(i=0;i<nb_streams;i++) {
ctx = c->rtp_ctx[i];
if (ctx) {
av_write_trailer(ctx);
av_free(ctx);
}
h = c->rtp_handles[i];
if (h)
url_close(h);
}
ctx = &c->fmt_ctx;
if (!c->last_packet_sent) {
if (ctx->oformat) {
/* prepare header */
if (url_open_dyn_buf(&ctx->pb) >= 0) {
av_write_trailer(ctx);
url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
}
}
}
for(i=0; i<ctx->nb_streams; i++)
av_free(ctx->streams[i]);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -