Newer
Older
* Copyright (c) 2000, 2001, 2002 Fabrice Bellard
Diego Biurrun
committed
* This file is part of FFmpeg.
*
* 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
Diego Biurrun
committed
* version 2.1 of the License, or (at your option) any later version.
Diego Biurrun
committed
* FFmpeg is distributed in the hope that it will be useful,
* 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
Diego Biurrun
committed
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Diego Biurrun
committed
/**
* @file
* multiple format streaming server based on the FFmpeg libraries
*/
#define closesocket close
#endif
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "libavformat/avformat.h"
// 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/rtpdec.h"
Martin Storsjö
committed
#include "libavformat/rtpproto.h"
#include "libavformat/rtsp.h"
#include "libavformat/rtspcodes.h"
#include "libavformat/avio_internal.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 "libavutil/intreadwrite.h"
#include "libavutil/mathematics.h"
#include "libavutil/random_seed.h"
#include "libavutil/time.h"
#if HAVE_UNISTD_H
Fabrice Bellard
committed
#include "cmdutils.h"
#include "ffserver_config.h"
const int program_birth_year = 2000;
static const OptionDef options[];
enum HTTPState {
HTTPSTATE_WAIT_REQUEST,
HTTPSTATE_SEND_HEADER,
HTTPSTATE_SEND_DATA_HEADER,
Fabrice Bellard
committed
HTTPSTATE_SEND_DATA, /* sending TCP or UDP data */
Fabrice Bellard
committed
HTTPSTATE_WAIT_FEED, /* wait for data from the feed */
HTTPSTATE_READY,
RTSPSTATE_WAIT_REQUEST,
RTSPSTATE_SEND_REPLY,
static const char * const http_state[] = {
Fabrice Bellard
committed
"HTTP_WAIT_REQUEST",
"HTTP_SEND_HEADER",
"SEND_DATA_HEADER",
"SEND_DATA",
"SEND_DATA_TRAILER",
"RECEIVE_DATA",
"WAIT_FEED",
Fabrice Bellard
committed
"READY",
"RTSP_WAIT_REQUEST",
"RTSP_SEND_REPLY",
Fabrice Bellard
committed
#define HTTP_REQUEST_TIMEOUT (15 * 1000)
#define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
typedef struct RTSPActionServerSetup {
uint32_t ipaddr;
char transport_option[512];
} RTSPActionServerSetup;
int64_t time1, time2;
/* context associated with one connection */
typedef struct HTTPContext {
enum HTTPState state;
int fd; /* socket file descriptor */
struct sockaddr_in from_addr; /* origin */
struct pollfd *poll_entry; /* used when polling */
Alex Beregszaszi
committed
int post;
Ronald S. Bultje
committed
int chunked_encoding;
int chunk_size; /* 0 if it needs to be read */
int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
/* feed input */
int feed_fd;
/* input format handling */
AVFormatContext *fmt_in;
int64_t start_time; /* In milliseconds - this wraps fairly often */
int64_t first_pts; /* initial pts value */
Fabrice Bellard
committed
int64_t cur_pts; /* current pts value from the stream in us */
int64_t cur_frame_duration; /* duration of the current frame in us */
int cur_frame_bytes; /* output frame size, needed to compute
the time at which we send each
packet */
int pts_stream_index; /* stream we choose as clock reference */
int64_t cur_clock; /* current clock reference value in us */
struct FFServerStream *stream;
int feed_streams[FFSERVER_MAX_STREAMS]; /* index of streams in the feed */
int switch_feed_streams[FFSERVER_MAX_STREAMS]; /* index of streams in the feed */
AVFormatContext fmt_ctx; /* instance of FFServerStream for one user */
int suppress_log;
int wmp_client_id;
char protocol[16];
char method[16];
char url[128];
Fabrice Bellard
committed
int is_packetized; /* if true, the stream is packetized */
int packet_stream_index; /* current stream for output in state machine */
Fabrice Bellard
committed
/* RTSP state specific */
uint8_t *pb_buffer; /* XXX: use that in all the code */
Fabrice Bellard
committed
int seq; /* RTSP sequence number */
Fabrice Bellard
committed
/* RTP state specific */
Ronald S. Bultje
committed
enum RTSPLowerTransport rtp_protocol;
Fabrice Bellard
committed
char session_id[32]; /* session id */
AVFormatContext *rtp_ctx[FFSERVER_MAX_STREAMS];
Fabrice Bellard
committed
URLContext *rtp_handles[FFSERVER_MAX_STREAMS];
/* RTP/TCP specific */
struct HTTPContext *rtsp_c;
uint8_t *packet_buffer, *packet_buffer_ptr, *packet_buffer_end;
} HTTPContext;
typedef struct FeedData {
long long data_count;
float avg_frame_size; /* frame size averaged over last frames with exponential mean */
static HTTPContext *first_http_ctx;
static FFServerConfig config = {
.nb_max_http_connections = 2000,
.nb_max_connections = 5,
.max_bandwidth = 1000,
Fabrice Bellard
committed
static void new_connection(int server_fd, int is_rtsp);
static void close_connection(HTTPContext *c);
/* HTTP handling */
static int handle_connection(HTTPContext *c);
static int http_send_data(HTTPContext *c);
static void compute_status(HTTPContext *c);
static int open_input_stream(HTTPContext *c, const char *info);
static int http_start_receive_data(HTTPContext *c);
static int http_receive_data(HTTPContext *c);
Fabrice Bellard
committed
/* RTSP handling */
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,
RTSPMessageHeader *h);
static void rtsp_cmd_play(HTTPContext *c, const char *url,
RTSPMessageHeader *h);
static void rtsp_cmd_interrupt(HTTPContext *c, const char *url,
RTSPMessageHeader *h, int pause_only);
Fabrice Bellard
committed
Fabrice Bellard
committed
/* SDP handling */
static int prepare_sdp_description(FFServerStream *stream, uint8_t **pbuffer,
Fabrice Bellard
committed
struct in_addr my_ip);
Fabrice Bellard
committed
/* RTP handling */
static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
FFServerStream *stream,
const char *session_id,
Ronald S. Bultje
committed
enum RTSPLowerTransport rtp_protocol);
static int rtp_new_av_stream(HTTPContext *c,
int stream_index, struct sockaddr_in *dest_addr,
HTTPContext *rtsp_c);
static int no_launch;
static int need_to_start_children;
/* maximum number of simultaneous HTTP connections */
static unsigned int nb_connections;
static int64_t cur_time; // Making this global saves on passing it around everywhere
static AVLFG random_state;
Michael Niedermayer
committed
static void htmlstrip(char *s) {
while (s && *s) {
s += strspn(s, "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,. ");
if (*s)
*s++ = '?';
}
}
static int64_t ffm_read_write_index(int fd)
{
uint8_t buf[8];
if (lseek(fd, 8, SEEK_SET) < 0)
return AVERROR(EIO);
if (read(fd, buf, 8) != 8)
return AVERROR(EIO);
return AV_RB64(buf);
}
static int ffm_write_write_index(int fd, int64_t pos)
{
uint8_t buf[8];
int i;
for(i=0;i<8;i++)
buf[i] = (pos >> (56 - i * 8)) & 0xff;
if (lseek(fd, 8, SEEK_SET) < 0)
return AVERROR(EIO);
if (write(fd, buf, 8) != 8)
return AVERROR(EIO);
return 8;
}
static void ffm_set_write_index(AVFormatContext *s, int64_t pos,
int64_t file_size)
{
FFMContext *ffm = s->priv_data;
ffm->write_index = pos;
ffm->file_size = file_size;
}
static char *ctime1(char *buf2, int buf_size)
{
time_t ti;
char *p;
ti = time(NULL);
p = ctime(&ti);
av_strlcpy(buf2, p, buf_size);
p = buf2 + strlen(p) - 1;
if (*p == '\n')
*p = '\0';
return buf2;
}
static void http_vlog(const char *fmt, va_list vargs)
if (logfile) {
}
print_prefix = strstr(fmt, "\n") != NULL;
fflush(logfile);
}
#ifdef __GNUC__
__attribute__ ((format (printf, 1, 2)))
#endif
static void http_log(const char *fmt, ...)
{
va_list vargs;
va_start(vargs, fmt);
http_vlog(fmt, vargs);
va_end(vargs);
}
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_get_level())
Baptiste Coudurier
committed
http_log("[%s @ %p]", avc->item_name(ptr), ptr);
print_prefix = strstr(fmt, "\n") != NULL;
http_vlog(fmt, vargs);
static void log_connection(HTTPContext *c)
{
return;
http_log("%s - - [%s] \"%s %s\" %d %"PRId64"\n",
inet_ntoa(c->from_addr.sin_addr), c->method, c->url,
c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
static void update_datarate(DataRateData *drd, int64_t count)
{
if (!drd->time1 && !drd->count1) {
drd->time1 = drd->time2 = cur_time;
drd->count1 = drd->count2 = count;
} else if (cur_time - drd->time2 > 5000) {
drd->time1 = drd->time2;
drd->count1 = drd->count2;
drd->time2 = cur_time;
drd->count2 = count;
}
}
/* In bytes per second */
static int compute_datarate(DataRateData *drd, int64_t count)
{
if (cur_time == drd->time1)
return 0;
return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
}
static void start_children(FFServerStream *feed)
char pathname[1024];
char *slash;
int i;
if (no_launch)
return;
/* replace "ffserver" with "ffmpeg" in the path of current
* program. Ignore user provided path */
av_strlcpy(pathname, my_program_name, sizeof(pathname));
slash = strrchr(pathname, '/');
if (!slash)
slash = pathname;
else
slash++;
strcpy(slash, "ffmpeg");
if (!feed->child_argv || feed->pid)
continue;
feed->pid_start = time(0);
feed->pid = fork();
if (feed->pid < 0) {
http_log("Unable to create children\n");
exit(1);
}
if (feed->pid)
continue;
http_log("Launch command line: ");
http_log("%s ", pathname);
for (i = 1; feed->child_argv[i] && feed->child_argv[i][0]; i++)
http_log("%s ", feed->child_argv[i]);
http_log("\n");
for (i = 3; i < 256; i++)
close(i);
Philip Gladstone
committed
if (!config.debug) {
if (!freopen("/dev/null", "r", stdin))
http_log("failed to redirect STDIN to /dev/null\n;");
if (!freopen("/dev/null", "w", stdout))
http_log("failed to redirect STDOUT to /dev/null\n;");
if (!freopen("/dev/null", "w", stderr))
http_log("failed to redirect STDERR to /dev/null\n;");
}
signal(SIGPIPE, SIG_DFL);
execvp(pathname, feed->child_argv);
_exit(1);
}
Fabrice Bellard
committed
/* open a listening socket */
static int socket_open_listen(struct sockaddr_in *my_addr)
Fabrice Bellard
committed
int server_fd, tmp;
server_fd = socket(AF_INET,SOCK_STREAM,0);
if (server_fd < 0) {
perror ("socket");
return -1;
}
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp)))
av_log(NULL, AV_LOG_WARNING, "setsockopt SO_REUSEADDR failed\n");
Nathan Caldwell
committed
my_addr->sin_family = AF_INET;
Fabrice Bellard
committed
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);
closesocket(server_fd);
if (ff_socket_nonblock(server_fd, 1) < 0)
av_log(NULL, AV_LOG_WARNING, "ff_socket_nonblock failed\n");
Fabrice Bellard
committed
return server_fd;
}
/* start all multicast streams */
static void start_multicast(void)
{
FFServerStream *stream;
char session_id[32];
HTTPContext *rtp_c;
int default_port, stream_index;
unsigned int random0, random1;
default_port = 6000;
for(stream = config.first_stream; stream; stream = stream->next) {
if (!stream->is_multicast)
continue;
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
random0 = av_lfg_get(&random_state);
random1 = av_lfg_get(&random_state);
/* open the RTP connection */
snprintf(session_id, sizeof(session_id), "%08x%08x",
random0, random1);
/* 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_LOWER_TRANSPORT_UDP_MULTICAST);
if (!rtp_c)
continue;
if (open_input_stream(rtp_c, "") < 0) {
http_log("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)
continue;
http_log("Could not open output stream '%s/streamid=%d'\n",
stream->filename, stream_index);
exit(1);
}
rtp_c->state = HTTPSTATE_SEND_DATA;
}
}
Fabrice Bellard
committed
/* main loop of the HTTP server */
Fabrice Bellard
committed
static int http_server(void)
{
struct pollfd *poll_table, *poll_entry;
Fabrice Bellard
committed
HTTPContext *c, *c_next;
poll_table = av_mallocz_array(config.nb_max_http_connections + 2,
sizeof(*poll_table));
if(!poll_table) {
http_log("Impossible to allocate a poll table handling %d "
"connections.\n", config.nb_max_http_connections);
return -1;
}
if (config.http_addr.sin_port) {
server_fd = socket_open_listen(&config.http_addr);
if (server_fd < 0) {
av_free(poll_table);
if (config.rtsp_addr.sin_port) {
rtsp_server_fd = socket_open_listen(&config.rtsp_addr);
if (rtsp_server_fd < 0) {
av_free(poll_table);
closesocket(server_fd);
}
if (!rtsp_server_fd && !server_fd) {
http_log("HTTP and RTSP disabled.\n");
av_free(poll_table);
start_children(config.first_feed);
start_multicast();
poll_entry->fd = server_fd;
poll_entry->events = POLLIN;
poll_entry++;
poll_entry->fd = rtsp_server_fd;
poll_entry->events = POLLIN;
poll_entry++;
Fabrice Bellard
committed
/* wait for events on each HTTP handle */
c = first_http_ctx;
Fabrice Bellard
committed
delay = 1000;
Fabrice Bellard
committed
case HTTPSTATE_SEND_HEADER:
case RTSPSTATE_SEND_REPLY:
Fabrice Bellard
committed
poll_entry->events = POLLOUT;
poll_entry++;
break;
case HTTPSTATE_SEND_DATA_HEADER:
case HTTPSTATE_SEND_DATA:
case HTTPSTATE_SEND_DATA_TRAILER:
Fabrice Bellard
committed
if (!c->is_packetized) {
/* for TCP, we output as much as we can
* (may need to put a limit) */
Fabrice Bellard
committed
c->poll_entry = poll_entry;
poll_entry->fd = fd;
poll_entry->events = POLLOUT;
poll_entry++;
} else {
Fabrice Bellard
committed
/* when ffserver is doing the timing, we work by
looking at which packet needs to be sent every
Fabrice Bellard
committed
10 ms */
/* one tick wait XXX: 10 ms assumed */
if (delay > 10)
delay = 10;
Fabrice Bellard
committed
}
Fabrice Bellard
committed
case HTTPSTATE_WAIT_REQUEST:
Fabrice Bellard
committed
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 {
Fabrice Bellard
committed
ret = poll(poll_table, poll_entry - poll_table, delay);
if (ret < 0 && ff_neterrno() != AVERROR(EAGAIN) &&
ff_neterrno() != AVERROR(EINTR)) {
av_free(poll_table);
return -1;
} while (ret < 0);
cur_time = av_gettime() / 1000;
if (need_to_start_children) {
need_to_start_children = 0;
start_children(config.first_feed);
for(c = first_http_ctx; c; c = c_next) {
Fabrice Bellard
committed
c_next = c->next;
if (handle_connection(c) < 0) {
log_connection(c);
/* close and free the connection */
Fabrice Bellard
committed
close_connection(c);
/* 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);
Fabrice Bellard
committed
/* start waiting for a new HTTP/RTSP request */
static void start_wait_request(HTTPContext *c, int is_rtsp)
Fabrice Bellard
committed
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 http_send_too_busy_reply(int fd)
{
char buffer[400];
int len = snprintf(buffer, sizeof(buffer),
"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 %u, and this exceeds the limit of %u.</p>\r\n"
"</body></html>\r\n",
nb_connections, config.nb_max_connections);
av_assert0(len < sizeof(buffer));
if (send(fd, buffer, len, 0) < len)
av_log(NULL, AV_LOG_WARNING, "Could not send too-busy reply, send() failed\n");
Fabrice Bellard
committed
static void new_connection(int server_fd, int is_rtsp)
{
struct sockaddr_in from_addr;
Fabrice Bellard
committed
HTTPContext *c = NULL;
len = sizeof(from_addr);
fd = accept(server_fd, (struct sockaddr *)&from_addr,
Fabrice Bellard
committed
&len);
if (fd < 0) {
http_log("error during accept %s\n", strerror(errno));
Fabrice Bellard
committed
return;
if (ff_socket_nonblock(fd, 1) < 0)
av_log(NULL, AV_LOG_WARNING, "ff_socket_nonblock failed\n");
Fabrice Bellard
committed
if (nb_connections >= config.nb_max_connections) {
http_send_too_busy_reply(fd);
Fabrice Bellard
committed
goto fail;
Fabrice Bellard
committed
/* add a new connection */
c = av_mallocz(sizeof(HTTPContext));
if (!c)
goto fail;
Fabrice Bellard
committed
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;
Fabrice Bellard
committed
nb_connections++;
Fabrice Bellard
committed
start_wait_request(c, is_rtsp);
return;
fail:
if (c) {
av_freep(&c->buffer);
Fabrice Bellard
committed
av_free(c);
}
closesocket(fd);
Fabrice Bellard
committed
}
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;
Fabrice Bellard
committed
c1 = *cp;
Fabrice Bellard
committed
*cp = c->next;
Fabrice Bellard
committed
cp = &c1->next;
}
/* remove references, if any (XXX: do it faster) */
for(c1 = first_http_ctx; c1; c1 = c1->next) {
if (c1->rtsp_c == c)
c1->rtsp_c = NULL;
}
Fabrice Bellard
committed
/* remove connection associated resources */
if (c->fd >= 0)
closesocket(c->fd);
Fabrice Bellard
committed
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)
Michael Niedermayer
committed
avcodec_close(st->codec);
Fabrice Bellard
committed
}
avformat_close_input(&c->fmt_in);
Fabrice Bellard
committed
}
/* free RTP output streams if any */
nb_streams = 0;
Fabrice Bellard
committed
nb_streams = c->stream->nb_streams;
Fabrice Bellard
committed
for(i=0;i<nb_streams;i++) {
ctx = c->rtp_ctx[i];
if (ctx) {
av_write_trailer(ctx);
av_dict_free(&ctx->metadata);
av_freep(&ctx->streams[0]);
av_freep(&ctx);
Fabrice Bellard
committed
}
h = c->rtp_handles[i];
Fabrice Bellard
committed
}
Mark Hills
committed
ctx = &c->fmt_ctx;
if (!c->last_packet_sent && c->state == HTTPSTATE_SEND_DATA_TRAILER) {
/* prepare header */
if (ctx->oformat && avio_open_dyn_buf(&ctx->pb) >= 0) {
av_write_trailer(ctx);
av_freep(&c->pb_buffer);
avio_close_dyn_buf(ctx->pb, &c->pb_buffer);
Philip Gladstone
committed
}
}
for(i=0; i<ctx->nb_streams; i++)
av_freep(&ctx->streams[i]);
av_freep(&ctx->streams);
av_freep(&ctx->priv_data);
Alex Beregszaszi
committed
if (c->stream && !c->post && c->stream->stream_type == STREAM_TYPE_LIVE)
current_bandwidth -= c->stream->bandwidth;
Alex Beregszaszi
committed
/* signal that there is no feed if we are the feeder socket */
if (c->state == HTTPSTATE_RECEIVE_DATA && c->stream) {
c->stream->feed_opened = 0;
close(c->feed_fd);
}
Fabrice Bellard
committed
av_freep(&c->pb_buffer);
av_freep(&c->buffer);
Fabrice Bellard
committed
av_free(c);
nb_connections--;
}
static int handle_connection(HTTPContext *c)
{
int len, ret;
Fabrice Bellard
committed
case RTSPSTATE_WAIT_REQUEST:
/* timeout ? */
if ((c->timeout - cur_time) < 0)
return -1;
if (c->poll_entry->revents & (POLLERR | POLLHUP))
return -1;
/* no need to read if no events */
if (!(c->poll_entry->revents & POLLIN))
return 0;
/* read the data */
Giancarlo Formicuccia
committed
read_loop:
len = recv(c->fd, c->buffer_ptr, 1, 0);
if (ff_neterrno() != AVERROR(EAGAIN) &&
ff_neterrno() != AVERROR(EINTR))
return -1;
} else if (len == 0) {
return -1;
} else {
c->buffer_ptr += len;
ptr = c->buffer_ptr;
if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
(ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
/* request found : parse it and reply */
Fabrice Bellard
committed
if (c->state == HTTPSTATE_WAIT_REQUEST) {
ret = http_parse_request(c);
} else {
ret = rtsp_parse_request(c);
}
if (ret < 0)
return -1;
} else if (ptr >= c->buffer_end) {
/* request too long: cannot do anything */
return -1;
Giancarlo Formicuccia
committed
} else goto read_loop;
}
break;
case HTTPSTATE_SEND_HEADER:
if (c->poll_entry->revents & (POLLERR | POLLHUP))
return -1;
Fabrice Bellard
committed
/* no need to write if no events */
len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
if (ff_neterrno() != AVERROR(EAGAIN) &&
ff_neterrno() != AVERROR(EINTR)) {
goto close_connection;
if (c->stream)
c->stream->bytes_served += len;
c->data_count += len;
Fabrice Bellard
committed
av_freep(&c->pb_buffer);
/* all the buffer was sent : synchronize to the incoming
* stream */
c->state = HTTPSTATE_SEND_DATA_HEADER;
c->buffer_ptr = c->buffer_end = c->buffer;
}
}
break;
case HTTPSTATE_SEND_DATA:
case HTTPSTATE_SEND_DATA_HEADER:
case HTTPSTATE_SEND_DATA_TRAILER:
Fabrice Bellard
committed
/* for packetized output, we consider we can always write (the
input streams set the speed). It may be better to verify
Fabrice Bellard
committed
that we do not rely too much on the kernel queues */
if (!c->is_packetized) {
if (c->poll_entry->revents & (POLLERR | POLLHUP))
return -1;
Fabrice Bellard
committed
/* no need to read if no events */
if (!(c->poll_entry->revents & POLLOUT))
return 0;
}
if (http_send_data(c) < 0)
/* close connection if trailer sent */
if (c->state == HTTPSTATE_SEND_DATA_TRAILER)
return -1;
break;
case HTTPSTATE_RECEIVE_DATA:
/* no need to read if no events */
if (c->poll_entry->revents & (POLLERR | POLLHUP))
return -1;
if (!(c->poll_entry->revents & POLLIN))
return 0;
if (http_receive_data(c) < 0)
return -1;
break;
case HTTPSTATE_WAIT_FEED:
/* no need to read if no events */
if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
return -1;
/* nothing to do, we'll be waken up by incoming feed packets */
break;
Fabrice Bellard
committed
case RTSPSTATE_SEND_REPLY:
if (c->poll_entry->revents & (POLLERR | POLLHUP))
goto close_connection;
Fabrice Bellard
committed
/* no need to write if no events */
if (!(c->poll_entry->revents & POLLOUT))
return 0;
len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
Fabrice Bellard
committed
if (len < 0) {
if (ff_neterrno() != AVERROR(EAGAIN) &&
ff_neterrno() != AVERROR(EINTR)) {
goto close_connection;
Fabrice Bellard
committed
}
} else {
c->buffer_ptr += len;
c->data_count += len;
if (c->buffer_ptr >= c->buffer_end) {
/* all the buffer was sent : wait for a new request */
av_freep(&c->pb_buffer);