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 */
Martin Storsjö
committed
#include "libavformat/rtpproto.h"
#include "libavformat/rtsp.h"
#include "libavformat/avio_internal.h"
#include "libavformat/internal.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/rational.h"
#include "libavutil/time.h"
#if HAVE_UNISTD_H
Fabrice Bellard
committed
#include "cmdutils.h"
#include "ffserver_config.h"
#define PATH_LENGTH 1024
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 *pfmt_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 inline void print_stream_params(AVIOContext *pb, FFServerStream *stream);
static void compute_status(HTTPContext *c);
static int http_parse_request(HTTPContext *c);
static int http_send_data(HTTPContext *c);
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);
Reynaldo H. Verdejo Pinochet
committed
static size_t htmlencode (const char *src, char **dest);
static inline void cp_html_entity (char *buffer, const char *entity);
static inline int check_codec_match(AVStream *ccf, AVStream *ccs, int stream);
static int no_launch;
static int need_to_start_children;
/* maximum number of simultaneous HTTP connections */
static unsigned int nb_connections;
/* Making this global saves on passing it around everywhere */
static int64_t cur_time;
static AVLFG random_state;
Reynaldo H. Verdejo Pinochet
committed
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
static inline void cp_html_entity (char *buffer, const char *entity) {
if (!buffer || !entity)
return;
while (*entity)
*buffer++ = *entity++;
}
/**
* Substitutes known conflicting chars on a text string with
* their corresponding HTML entities.
*
* Returns the number of bytes in the 'encoded' representation
* not including the terminating NUL.
*/
static size_t htmlencode (const char *src, char **dest) {
const char *amp = "&";
const char *lt = "<";
const char *gt = ">";
const char *start;
char *tmp;
size_t final_size = 0;
if (!src)
return 0;
start = src;
/* Compute needed dest size */
while (*src != '\0') {
switch(*src) {
case 38: /* & */
final_size += 5;
break;
case 60: /* < */
case 62: /* > */
final_size += 4;
break;
default:
final_size++;
}
src++;
}
src = start;
*dest = av_mallocz(final_size + 1);
if (!*dest)
return 0;
/* Build dest */
tmp = *dest;
while (*src != '\0') {
switch(*src) {
case 38: /* & */
cp_html_entity (tmp, amp);
tmp += 5;
break;
case 60: /* < */
cp_html_entity (tmp, lt);
tmp += 4;
break;
case 62: /* > */
cp_html_entity (tmp, gt);
tmp += 4;
break;
default:
*tmp = *src;
tmp += 1;
}
src++;
Michael Niedermayer
committed
}
Reynaldo H. Verdejo Pinochet
committed
*tmp = '\0';
return final_size;
Michael Niedermayer
committed
}
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)
goto bail_eio;
if (write(fd, buf, 8) != 8)
goto bail_eio;
return 8;
bail_eio:
return AVERROR(EIO);
}
static void ffm_set_write_index(AVFormatContext *s, int64_t pos,
int64_t file_size)
{
Michael Niedermayer
committed
av_opt_set_int(s, "server_attached", 1, AV_OPT_SEARCH_CHILDREN);
Oliver Collyer
committed
av_opt_set_int(s, "ffm_write_index", pos, AV_OPT_SEARCH_CHILDREN);
av_opt_set_int(s, "ffm_file_size", file_size, AV_OPT_SEARCH_CHILDREN);
static char *ctime1(char *buf2, size_t buf_size)
{
time_t ti;
char *p;
ti = time(NULL);
p = ctime(&ti);
if (!p || !*p) {
*buf2 = '\0';
return buf2;
}
av_strlcpy(buf2, p, buf_size);
if (*p == '\n')
*p = '\0';
return buf2;
}
static void http_vlog(const char *fmt, va_list vargs)
if (!logfile)
return;
if (print_prefix) {
ctime1(buf, sizeof(buf));
fprintf(logfile, "%s ", buf);
}
print_prefix = strstr(fmt, "\n") != NULL;
vfprintf(logfile, fmt, vargs);
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;
char *slash;
int i;
size_t cmd_length;
if (no_launch)
return;
cmd_length = strlen(my_program_name);
/**
* FIXME: WIP Safeguard. Remove after clearing all harcoded
* '1024' path lengths
*/
if (cmd_length > PATH_LENGTH - 1) {
http_log("Could not start children. Command line: '%s' exceeds "
"path length limit (%d)\n", my_program_name, PATH_LENGTH);
return;
}
pathname = av_strdup (my_program_name);
if (!pathname) {
http_log("Could not allocate memory for children cmd line\n");
return;
}
/* replace "ffserver" with "ffmpeg" in the path of current
* program. Ignore user provided path */
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: %s\n", strerror(errno));
av_free (pathname);
exit(EXIT_FAILURE);
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);
av_free (pathname);
av_free (pathname);
}
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);
goto fail;
goto fail;
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;
fail:
closesocket(server_fd);
return -1;
Fabrice Bellard
committed
}
/* 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;
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);
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
/* 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)
goto quit;
if (config.rtsp_addr.sin_port) {
rtsp_server_fd = socket_open_listen(&config.rtsp_addr);
if (rtsp_server_fd < 0) {
closesocket(server_fd);
}
if (!rtsp_server_fd && !server_fd) {
http_log("HTTP and RTSP disabled.\n");
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
* 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 */
Fabrice Bellard
committed
ret = poll(poll_table, poll_entry - poll_table, delay);
if (ret < 0 && ff_neterrno() != AVERROR(EAGAIN) &&
ff_neterrno() != AVERROR(EINTR)) {
} 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);
quit:
av_free(poll_table);
return -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' */
c->state = is_rtsp ? RTSPSTATE_WAIT_REQUEST : HTTPSTATE_WAIT_REQUEST;
c->timeout = cur_time +
(is_rtsp ? RTSP_REQUEST_TIMEOUT : HTTP_REQUEST_TIMEOUT);
Fabrice Bellard
committed
}
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"
"<!DOCTYPE html>\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;
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
}
ffurl_close(c->rtp_handles[i]);
Fabrice Bellard
committed
}
Mark Hills
committed
if (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);
}
}
for(i=0; i<ctx->nb_streams; i++)
av_freep(&ctx->streams[i]);
av_freep(&ctx->streams);
av_freep(&ctx->priv_data);
Philip Gladstone
committed
}
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:
if (!(len = recv(c->fd, c->buffer_ptr, 1, 0)))
return -1;
if (ff_neterrno() != AVERROR(EAGAIN) &&
ff_neterrno() != AVERROR(EINTR))
/* search for end of request. */
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 */
if (c->state == HTTPSTATE_WAIT_REQUEST)