2
* Multiple format streaming server
3
* Copyright (c) 2000, 2001, 2002 Fabrice Bellard
5
* This file is part of Libav.
7
* Libav is free software; you can redistribute it and/or
8
* modify it under the terms of the GNU Lesser General Public
9
* License as published by the Free Software Foundation; either
10
* version 2.1 of the License, or (at your option) any later version.
12
* Libav is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
* Lesser General Public License for more details.
17
* You should have received a copy of the GNU Lesser General Public
18
* License along with Libav; if not, write to the Free Software
19
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
24
#define closesocket close
28
#include "libavformat/avformat.h"
29
#include "libavformat/ffm.h"
30
#include "libavformat/network.h"
31
#include "libavformat/os_support.h"
32
#include "libavformat/rtpdec.h"
33
#include "libavformat/rtsp.h"
34
// XXX for ffio_open_dyn_packet_buffer, to be removed
35
#include "libavformat/avio_internal.h"
36
#include "libavutil/avstring.h"
37
#include "libavutil/lfg.h"
38
#include "libavutil/dict.h"
39
#include "libavutil/mathematics.h"
40
#include "libavutil/random_seed.h"
41
#include "libavutil/parseutils.h"
42
#include "libavutil/opt.h"
46
#include <sys/ioctl.h>
61
const char program_name[] = "avserver";
62
const int program_birth_year = 2000;
64
static const OptionDef options[];
67
HTTPSTATE_WAIT_REQUEST,
68
HTTPSTATE_SEND_HEADER,
69
HTTPSTATE_SEND_DATA_HEADER,
70
HTTPSTATE_SEND_DATA, /* sending TCP or UDP data */
71
HTTPSTATE_SEND_DATA_TRAILER,
72
HTTPSTATE_RECEIVE_DATA,
73
HTTPSTATE_WAIT_FEED, /* wait for data from the feed */
76
RTSPSTATE_WAIT_REQUEST,
78
RTSPSTATE_SEND_PACKET,
81
static const char *http_state[] = {
97
#define MAX_STREAMS 20
99
#define IOBUFFER_INIT_SIZE 8192
101
/* timeouts are in ms */
102
#define HTTP_REQUEST_TIMEOUT (15 * 1000)
103
#define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
105
#define SYNC_TIMEOUT (10 * 1000)
107
typedef struct RTSPActionServerSetup {
109
char transport_option[512];
110
} RTSPActionServerSetup;
113
int64_t count1, count2;
114
int64_t time1, time2;
117
/* context associated with one connection */
118
typedef struct HTTPContext {
119
enum HTTPState state;
120
int fd; /* socket file descriptor */
121
struct sockaddr_in from_addr; /* origin */
122
struct pollfd *poll_entry; /* used when polling */
124
uint8_t *buffer_ptr, *buffer_end;
127
int chunked_encoding;
128
int chunk_size; /* 0 if it needs to be read */
129
struct HTTPContext *next;
130
int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
134
/* input format handling */
135
AVFormatContext *fmt_in;
136
int64_t start_time; /* In milliseconds - this wraps fairly often */
137
int64_t first_pts; /* initial pts value */
138
int64_t cur_pts; /* current pts value from the stream in us */
139
int64_t cur_frame_duration; /* duration of the current frame in us */
140
int cur_frame_bytes; /* output frame size, needed to compute
141
the time at which we send each
143
int pts_stream_index; /* stream we choose as clock reference */
144
int64_t cur_clock; /* current clock reference value in us */
145
/* output format handling */
146
struct FFStream *stream;
147
/* -1 is invalid stream */
148
int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
149
int switch_feed_streams[MAX_STREAMS]; /* index of streams in the feed */
151
AVFormatContext fmt_ctx; /* instance of FFStream for one user */
152
int last_packet_sent; /* true if last data packet was sent */
154
DataRateData datarate;
161
int is_packetized; /* if true, the stream is packetized */
162
int packet_stream_index; /* current stream for output in state machine */
164
/* RTSP state specific */
165
uint8_t *pb_buffer; /* XXX: use that in all the code */
167
int seq; /* RTSP sequence number */
169
/* RTP state specific */
170
enum RTSPLowerTransport rtp_protocol;
171
char session_id[32]; /* session id */
172
AVFormatContext *rtp_ctx[MAX_STREAMS];
174
/* RTP/UDP specific */
175
URLContext *rtp_handles[MAX_STREAMS];
177
/* RTP/TCP specific */
178
struct HTTPContext *rtsp_c;
179
uint8_t *packet_buffer, *packet_buffer_ptr, *packet_buffer_end;
182
/* each generated stream is described here */
186
STREAM_TYPE_REDIRECT,
189
enum IPAddressAction {
194
typedef struct IPAddressACL {
195
struct IPAddressACL *next;
196
enum IPAddressAction action;
197
/* These are in host order */
198
struct in_addr first;
202
/* description of each stream of the avserver.conf file */
203
typedef struct FFStream {
204
enum StreamType stream_type;
205
char filename[1024]; /* stream filename */
206
struct FFStream *feed; /* feed we are using (can be null if
208
AVDictionary *in_opts; /* input parameters */
209
AVInputFormat *ifmt; /* if non NULL, force input format */
212
char dynamic_acl[1024];
214
int prebuffer; /* Number of millseconds early to start */
215
int64_t max_time; /* Number of milliseconds to run */
217
AVStream *streams[MAX_STREAMS];
218
int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
219
char feed_filename[1024]; /* file name of the feed storage, or
220
input file name for a stream */
225
pid_t pid; /* of avconv process */
226
time_t pid_start; /* of avconv process */
228
struct FFStream *next;
229
unsigned bandwidth; /* bandwidth, in kbits/s */
232
/* multicast specific */
234
struct in_addr multicast_ip;
235
int multicast_port; /* first port used for multicast */
237
int loop; /* if true, send the stream in loops (only meaningful if file) */
240
int feed_opened; /* true if someone is writing to the feed */
241
int is_feed; /* true if it is a feed */
242
int readonly; /* True if writing is prohibited to the file */
243
int truncate; /* True if feeder connection truncate the feed file */
245
int64_t bytes_served;
246
int64_t feed_max_size; /* maximum storage size, zero means unlimited */
247
int64_t feed_write_index; /* current write position in feed (it wraps around) */
248
int64_t feed_size; /* current size of feed */
249
struct FFStream *next_feed;
252
typedef struct FeedData {
253
long long data_count;
254
float avg_frame_size; /* frame size averaged over last frames with exponential mean */
257
static struct sockaddr_in my_http_addr;
258
static struct sockaddr_in my_rtsp_addr;
260
static char logfilename[1024];
261
static HTTPContext *first_http_ctx;
262
static FFStream *first_feed; /* contains only feeds */
263
static FFStream *first_stream; /* contains all streams, including feeds */
265
static void new_connection(int server_fd, int is_rtsp);
266
static void close_connection(HTTPContext *c);
269
static int handle_connection(HTTPContext *c);
270
static int http_parse_request(HTTPContext *c);
271
static int http_send_data(HTTPContext *c);
272
static void compute_status(HTTPContext *c);
273
static int open_input_stream(HTTPContext *c, const char *info);
274
static int http_start_receive_data(HTTPContext *c);
275
static int http_receive_data(HTTPContext *c);
278
static int rtsp_parse_request(HTTPContext *c);
279
static void rtsp_cmd_describe(HTTPContext *c, const char *url);
280
static void rtsp_cmd_options(HTTPContext *c, const char *url);
281
static void rtsp_cmd_setup(HTTPContext *c, const char *url, RTSPMessageHeader *h);
282
static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPMessageHeader *h);
283
static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPMessageHeader *h);
284
static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPMessageHeader *h);
287
static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
288
struct in_addr my_ip);
291
static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
292
FFStream *stream, const char *session_id,
293
enum RTSPLowerTransport rtp_protocol);
294
static int rtp_new_av_stream(HTTPContext *c,
295
int stream_index, struct sockaddr_in *dest_addr,
296
HTTPContext *rtsp_c);
298
static const char *my_program_name;
299
static const char *my_program_dir;
301
static const char *config_filename = "/etc/avserver.conf";
303
static int avserver_debug;
304
static int avserver_daemon;
305
static int no_launch;
306
static int need_to_start_children;
308
/* maximum number of simultaneous HTTP connections */
309
static unsigned int nb_max_http_connections = 2000;
310
static unsigned int nb_max_connections = 5;
311
static unsigned int nb_connections;
313
static uint64_t max_bandwidth = 1000;
314
static uint64_t current_bandwidth;
316
static int64_t cur_time; // Making this global saves on passing it around everywhere
318
static AVLFG random_state;
320
static FILE *logfile = NULL;
322
void exit_program(int ret)
327
/* FIXME: make avserver work with IPv6 */
328
/* resolve host with also IP address parsing */
329
static int resolve_host(struct in_addr *sin_addr, const char *hostname)
332
if (!ff_inet_aton(hostname, sin_addr)) {
334
struct addrinfo *ai, *cur;
335
struct addrinfo hints;
336
memset(&hints, 0, sizeof(hints));
337
hints.ai_family = AF_INET;
338
if (getaddrinfo(hostname, NULL, &hints, &ai))
340
/* getaddrinfo returns a linked list of addrinfo structs.
341
* Even if we set ai_family = AF_INET above, make sure
342
* that the returned one actually is of the correct type. */
343
for (cur = ai; cur; cur = cur->ai_next) {
344
if (cur->ai_family == AF_INET) {
345
*sin_addr = ((struct sockaddr_in *)cur->ai_addr)->sin_addr;
354
hp = gethostbyname(hostname);
357
memcpy(sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
363
static char *ctime1(char *buf2)
371
p = buf2 + strlen(p) - 1;
377
static void http_vlog(const char *fmt, va_list vargs)
379
static int print_prefix = 1;
384
fprintf(logfile, "%s ", buf);
386
print_prefix = strstr(fmt, "\n") != NULL;
387
vfprintf(logfile, fmt, vargs);
393
__attribute__ ((format (printf, 1, 2)))
395
static void http_log(const char *fmt, ...)
398
va_start(vargs, fmt);
399
http_vlog(fmt, vargs);
403
static void http_av_log(void *ptr, int level, const char *fmt, va_list vargs)
405
static int print_prefix = 1;
406
AVClass *avc = ptr ? *(AVClass**)ptr : NULL;
407
if (level > av_log_get_level())
409
if (print_prefix && avc)
410
http_log("[%s @ %p]", avc->item_name(ptr), ptr);
411
print_prefix = strstr(fmt, "\n") != NULL;
412
http_vlog(fmt, vargs);
415
static void log_connection(HTTPContext *c)
420
http_log("%s - - [%s] \"%s %s\" %d %"PRId64"\n",
421
inet_ntoa(c->from_addr.sin_addr), c->method, c->url,
422
c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
425
static void update_datarate(DataRateData *drd, int64_t count)
427
if (!drd->time1 && !drd->count1) {
428
drd->time1 = drd->time2 = cur_time;
429
drd->count1 = drd->count2 = count;
430
} else if (cur_time - drd->time2 > 5000) {
431
drd->time1 = drd->time2;
432
drd->count1 = drd->count2;
433
drd->time2 = cur_time;
438
/* In bytes per second */
439
static int compute_datarate(DataRateData *drd, int64_t count)
441
if (cur_time == drd->time1)
444
return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
448
static void start_children(FFStream *feed)
453
for (; feed; feed = feed->next) {
454
if (feed->child_argv && !feed->pid) {
455
feed->pid_start = time(0);
460
http_log("Unable to create children\n");
469
av_strlcpy(pathname, my_program_name, sizeof(pathname));
471
slash = strrchr(pathname, '/');
476
strcpy(slash, "avconv");
478
http_log("Launch command line: ");
479
http_log("%s ", pathname);
480
for (i = 1; feed->child_argv[i] && feed->child_argv[i][0]; i++)
481
http_log("%s ", feed->child_argv[i]);
484
for (i = 3; i < 256; i++)
487
if (!avserver_debug) {
488
i = open("/dev/null", O_RDWR);
497
/* This is needed to make relative pathnames work */
498
chdir(my_program_dir);
500
signal(SIGPIPE, SIG_DFL);
502
execvp(pathname, feed->child_argv);
510
/* open a listening socket */
511
static int socket_open_listen(struct sockaddr_in *my_addr)
515
server_fd = socket(AF_INET,SOCK_STREAM,0);
522
setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
524
my_addr->sin_family = AF_INET;
525
if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
527
snprintf(bindmsg, sizeof(bindmsg), "bind(port %d)", ntohs(my_addr->sin_port));
529
closesocket(server_fd);
533
if (listen (server_fd, 5) < 0) {
535
closesocket(server_fd);
538
ff_socket_nonblock(server_fd, 1);
543
/* start all multicast streams */
544
static void start_multicast(void)
549
struct sockaddr_in dest_addr;
550
int default_port, stream_index;
553
for(stream = first_stream; stream != NULL; stream = stream->next) {
554
if (stream->is_multicast) {
555
/* open the RTP connection */
556
snprintf(session_id, sizeof(session_id), "%08x%08x",
557
av_lfg_get(&random_state), av_lfg_get(&random_state));
559
/* choose a port if none given */
560
if (stream->multicast_port == 0) {
561
stream->multicast_port = default_port;
565
dest_addr.sin_family = AF_INET;
566
dest_addr.sin_addr = stream->multicast_ip;
567
dest_addr.sin_port = htons(stream->multicast_port);
569
rtp_c = rtp_new_connection(&dest_addr, stream, session_id,
570
RTSP_LOWER_TRANSPORT_UDP_MULTICAST);
574
if (open_input_stream(rtp_c, "") < 0) {
575
http_log("Could not open input stream for stream '%s'\n",
580
/* open each RTP stream */
581
for(stream_index = 0; stream_index < stream->nb_streams;
583
dest_addr.sin_port = htons(stream->multicast_port +
585
if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, NULL) < 0) {
586
http_log("Could not open output stream '%s/streamid=%d'\n",
587
stream->filename, stream_index);
592
/* change state to send data */
593
rtp_c->state = HTTPSTATE_SEND_DATA;
598
/* main loop of the http server */
599
static int http_server(void)
601
int server_fd = 0, rtsp_server_fd = 0;
602
int ret, delay, delay1;
603
struct pollfd *poll_table, *poll_entry;
604
HTTPContext *c, *c_next;
606
if(!(poll_table = av_mallocz((nb_max_http_connections + 2)*sizeof(*poll_table)))) {
607
http_log("Impossible to allocate a poll table handling %d connections.\n", nb_max_http_connections);
611
if (my_http_addr.sin_port) {
612
server_fd = socket_open_listen(&my_http_addr);
617
if (my_rtsp_addr.sin_port) {
618
rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
619
if (rtsp_server_fd < 0)
623
if (!rtsp_server_fd && !server_fd) {
624
http_log("HTTP and RTSP disabled.\n");
628
http_log("AVserver started.\n");
630
start_children(first_feed);
635
poll_entry = poll_table;
637
poll_entry->fd = server_fd;
638
poll_entry->events = POLLIN;
641
if (rtsp_server_fd) {
642
poll_entry->fd = rtsp_server_fd;
643
poll_entry->events = POLLIN;
647
/* wait for events on each HTTP handle */
654
case HTTPSTATE_SEND_HEADER:
655
case RTSPSTATE_SEND_REPLY:
656
case RTSPSTATE_SEND_PACKET:
657
c->poll_entry = poll_entry;
659
poll_entry->events = POLLOUT;
662
case HTTPSTATE_SEND_DATA_HEADER:
663
case HTTPSTATE_SEND_DATA:
664
case HTTPSTATE_SEND_DATA_TRAILER:
665
if (!c->is_packetized) {
666
/* for TCP, we output as much as we can (may need to put a limit) */
667
c->poll_entry = poll_entry;
669
poll_entry->events = POLLOUT;
672
/* when avserver is doing the timing, we work by
673
looking at which packet need to be sent every
675
delay1 = 10; /* one tick wait XXX: 10 ms assumed */
680
case HTTPSTATE_WAIT_REQUEST:
681
case HTTPSTATE_RECEIVE_DATA:
682
case HTTPSTATE_WAIT_FEED:
683
case RTSPSTATE_WAIT_REQUEST:
684
/* need to catch errors */
685
c->poll_entry = poll_entry;
687
poll_entry->events = POLLIN;/* Maybe this will work */
691
c->poll_entry = NULL;
697
/* wait for an event on one connection. We poll at least every
698
second to handle timeouts */
700
ret = poll(poll_table, poll_entry - poll_table, delay);
701
if (ret < 0 && ff_neterrno() != AVERROR(EAGAIN) &&
702
ff_neterrno() != AVERROR(EINTR))
706
cur_time = av_gettime() / 1000;
708
if (need_to_start_children) {
709
need_to_start_children = 0;
710
start_children(first_feed);
713
/* now handle the events */
714
for(c = first_http_ctx; c != NULL; c = c_next) {
716
if (handle_connection(c) < 0) {
717
/* close and free the connection */
723
poll_entry = poll_table;
725
/* new HTTP connection request ? */
726
if (poll_entry->revents & POLLIN)
727
new_connection(server_fd, 0);
730
if (rtsp_server_fd) {
731
/* new RTSP connection request ? */
732
if (poll_entry->revents & POLLIN)
733
new_connection(rtsp_server_fd, 1);
738
/* start waiting for a new HTTP/RTSP request */
739
static void start_wait_request(HTTPContext *c, int is_rtsp)
741
c->buffer_ptr = c->buffer;
742
c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */
745
c->timeout = cur_time + RTSP_REQUEST_TIMEOUT;
746
c->state = RTSPSTATE_WAIT_REQUEST;
748
c->timeout = cur_time + HTTP_REQUEST_TIMEOUT;
749
c->state = HTTPSTATE_WAIT_REQUEST;
753
static void http_send_too_busy_reply(int fd)
756
int len = snprintf(buffer, sizeof(buffer),
757
"HTTP/1.0 503 Server too busy\r\n"
758
"Content-type: text/html\r\n"
760
"<html><head><title>Too busy</title></head><body>\r\n"
761
"<p>The server is too busy to serve your request at this time.</p>\r\n"
762
"<p>The number of current connections is %d, and this exceeds the limit of %d.</p>\r\n"
763
"</body></html>\r\n",
764
nb_connections, nb_max_connections);
765
send(fd, buffer, len, 0);
769
static void new_connection(int server_fd, int is_rtsp)
771
struct sockaddr_in from_addr;
773
HTTPContext *c = NULL;
775
len = sizeof(from_addr);
776
fd = accept(server_fd, (struct sockaddr *)&from_addr,
779
http_log("error during accept %s\n", strerror(errno));
782
ff_socket_nonblock(fd, 1);
784
if (nb_connections >= nb_max_connections) {
785
http_send_too_busy_reply(fd);
789
/* add a new connection */
790
c = av_mallocz(sizeof(HTTPContext));
795
c->poll_entry = NULL;
796
c->from_addr = from_addr;
797
c->buffer_size = IOBUFFER_INIT_SIZE;
798
c->buffer = av_malloc(c->buffer_size);
802
c->next = first_http_ctx;
806
start_wait_request(c, is_rtsp);
818
static void close_connection(HTTPContext *c)
820
HTTPContext **cp, *c1;
822
AVFormatContext *ctx;
826
/* remove connection from list */
827
cp = &first_http_ctx;
828
while ((*cp) != NULL) {
836
/* remove references, if any (XXX: do it faster) */
837
for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
842
/* remove connection associated resources */
846
/* close each frame parser */
847
for(i=0;i<c->fmt_in->nb_streams;i++) {
848
st = c->fmt_in->streams[i];
849
if (st->codec->codec)
850
avcodec_close(st->codec);
852
avformat_close_input(&c->fmt_in);
855
/* free RTP output streams if any */
858
nb_streams = c->stream->nb_streams;
860
for(i=0;i<nb_streams;i++) {
863
av_write_trailer(ctx);
864
av_dict_free(&ctx->metadata);
865
av_free(ctx->streams[0]);
868
h = c->rtp_handles[i];
875
if (!c->last_packet_sent && c->state == HTTPSTATE_SEND_DATA_TRAILER) {
878
if (avio_open_dyn_buf(&ctx->pb) >= 0) {
879
av_write_trailer(ctx);
880
av_freep(&c->pb_buffer);
881
avio_close_dyn_buf(ctx->pb, &c->pb_buffer);
886
for(i=0; i<ctx->nb_streams; i++)
887
av_free(ctx->streams[i]);
889
if (c->stream && !c->post && c->stream->stream_type == STREAM_TYPE_LIVE)
890
current_bandwidth -= c->stream->bandwidth;
892
/* signal that there is no feed if we are the feeder socket */
893
if (c->state == HTTPSTATE_RECEIVE_DATA && c->stream) {
894
c->stream->feed_opened = 0;
898
av_freep(&c->pb_buffer);
899
av_freep(&c->packet_buffer);
905
static int handle_connection(HTTPContext *c)
910
case HTTPSTATE_WAIT_REQUEST:
911
case RTSPSTATE_WAIT_REQUEST:
913
if ((c->timeout - cur_time) < 0)
915
if (c->poll_entry->revents & (POLLERR | POLLHUP))
918
/* no need to read if no events */
919
if (!(c->poll_entry->revents & POLLIN))
923
len = recv(c->fd, c->buffer_ptr, 1, 0);
925
if (ff_neterrno() != AVERROR(EAGAIN) &&
926
ff_neterrno() != AVERROR(EINTR))
928
} else if (len == 0) {
931
/* search for end of request. */
933
c->buffer_ptr += len;
935
if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
936
(ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
937
/* request found : parse it and reply */
938
if (c->state == HTTPSTATE_WAIT_REQUEST) {
939
ret = http_parse_request(c);
941
ret = rtsp_parse_request(c);
945
} else if (ptr >= c->buffer_end) {
946
/* request too long: cannot do anything */
948
} else goto read_loop;
952
case HTTPSTATE_SEND_HEADER:
953
if (c->poll_entry->revents & (POLLERR | POLLHUP))
956
/* no need to write if no events */
957
if (!(c->poll_entry->revents & POLLOUT))
959
len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
961
if (ff_neterrno() != AVERROR(EAGAIN) &&
962
ff_neterrno() != AVERROR(EINTR)) {
963
/* error : close connection */
964
av_freep(&c->pb_buffer);
968
c->buffer_ptr += len;
970
c->stream->bytes_served += len;
971
c->data_count += len;
972
if (c->buffer_ptr >= c->buffer_end) {
973
av_freep(&c->pb_buffer);
977
/* all the buffer was sent : synchronize to the incoming stream */
978
c->state = HTTPSTATE_SEND_DATA_HEADER;
979
c->buffer_ptr = c->buffer_end = c->buffer;
984
case HTTPSTATE_SEND_DATA:
985
case HTTPSTATE_SEND_DATA_HEADER:
986
case HTTPSTATE_SEND_DATA_TRAILER:
987
/* for packetized output, we consider we can always write (the
988
input streams sets the speed). It may be better to verify
989
that we do not rely too much on the kernel queues */
990
if (!c->is_packetized) {
991
if (c->poll_entry->revents & (POLLERR | POLLHUP))
994
/* no need to read if no events */
995
if (!(c->poll_entry->revents & POLLOUT))
998
if (http_send_data(c) < 0)
1000
/* close connection if trailer sent */
1001
if (c->state == HTTPSTATE_SEND_DATA_TRAILER)
1004
case HTTPSTATE_RECEIVE_DATA:
1005
/* no need to read if no events */
1006
if (c->poll_entry->revents & (POLLERR | POLLHUP))
1008
if (!(c->poll_entry->revents & POLLIN))
1010
if (http_receive_data(c) < 0)
1013
case HTTPSTATE_WAIT_FEED:
1014
/* no need to read if no events */
1015
if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
1018
/* nothing to do, we'll be waken up by incoming feed packets */
1021
case RTSPSTATE_SEND_REPLY:
1022
if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
1023
av_freep(&c->pb_buffer);
1026
/* no need to write if no events */
1027
if (!(c->poll_entry->revents & POLLOUT))
1029
len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
1031
if (ff_neterrno() != AVERROR(EAGAIN) &&
1032
ff_neterrno() != AVERROR(EINTR)) {
1033
/* error : close connection */
1034
av_freep(&c->pb_buffer);
1038
c->buffer_ptr += len;
1039
c->data_count += len;
1040
if (c->buffer_ptr >= c->buffer_end) {
1041
/* all the buffer was sent : wait for a new request */
1042
av_freep(&c->pb_buffer);
1043
start_wait_request(c, 1);
1047
case RTSPSTATE_SEND_PACKET:
1048
if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
1049
av_freep(&c->packet_buffer);
1052
/* no need to write if no events */
1053
if (!(c->poll_entry->revents & POLLOUT))
1055
len = send(c->fd, c->packet_buffer_ptr,
1056
c->packet_buffer_end - c->packet_buffer_ptr, 0);
1058
if (ff_neterrno() != AVERROR(EAGAIN) &&
1059
ff_neterrno() != AVERROR(EINTR)) {
1060
/* error : close connection */
1061
av_freep(&c->packet_buffer);
1065
c->packet_buffer_ptr += len;
1066
if (c->packet_buffer_ptr >= c->packet_buffer_end) {
1067
/* all the buffer was sent : wait for a new request */
1068
av_freep(&c->packet_buffer);
1069
c->state = RTSPSTATE_WAIT_REQUEST;
1073
case HTTPSTATE_READY:
1082
static int extract_rates(char *rates, int ratelen, const char *request)
1086
for (p = request; *p && *p != '\r' && *p != '\n'; ) {
1087
if (av_strncasecmp(p, "Pragma:", 7) == 0) {
1088
const char *q = p + 7;
1090
while (*q && *q != '\n' && isspace(*q))
1093
if (av_strncasecmp(q, "stream-switch-entry=", 20) == 0) {
1099
memset(rates, 0xff, ratelen);
1102
while (*q && *q != '\n' && *q != ':')
1105
if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2)
1109
if (stream_no < ratelen && stream_no >= 0)
1110
rates[stream_no] = rate_no;
1112
while (*q && *q != '\n' && !isspace(*q))
1119
p = strchr(p, '\n');
1129
static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
1132
int best_bitrate = 100000000;
1135
for (i = 0; i < feed->nb_streams; i++) {
1136
AVCodecContext *feed_codec = feed->streams[i]->codec;
1138
if (feed_codec->codec_id != codec->codec_id ||
1139
feed_codec->sample_rate != codec->sample_rate ||
1140
feed_codec->width != codec->width ||
1141
feed_codec->height != codec->height)
1144
/* Potential stream */
1146
/* We want the fastest stream less than bit_rate, or the slowest
1147
* faster than bit_rate
1150
if (feed_codec->bit_rate <= bit_rate) {
1151
if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) {
1152
best_bitrate = feed_codec->bit_rate;
1156
if (feed_codec->bit_rate < best_bitrate) {
1157
best_bitrate = feed_codec->bit_rate;
1166
static int modify_current_stream(HTTPContext *c, char *rates)
1169
FFStream *req = c->stream;
1170
int action_required = 0;
1172
/* Not much we can do for a feed */
1176
for (i = 0; i < req->nb_streams; i++) {
1177
AVCodecContext *codec = req->streams[i]->codec;
1181
c->switch_feed_streams[i] = req->feed_streams[i];
1184
c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
1187
/* Wants off or slow */
1188
c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
1190
/* This doesn't work well when it turns off the only stream! */
1191
c->switch_feed_streams[i] = -2;
1192
c->feed_streams[i] = -2;
1197
if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
1198
action_required = 1;
1201
return action_required;
1204
/* XXX: factorize in utils.c ? */
1205
/* XXX: take care with different space meaning */
1206
static void skip_spaces(const char **pp)
1210
while (*p == ' ' || *p == '\t')
1215
static void get_word(char *buf, int buf_size, const char **pp)
1223
while (!isspace(*p) && *p != '\0') {
1224
if ((q - buf) < buf_size - 1)
1233
static void get_arg(char *buf, int buf_size, const char **pp)
1240
while (isspace(*p)) p++;
1243
if (*p == '\"' || *p == '\'')
1255
if ((q - buf) < buf_size - 1)
1260
if (quote && *p == quote)
1265
static void parse_acl_row(FFStream *stream, FFStream* feed, IPAddressACL *ext_acl,
1266
const char *p, const char *filename, int line_num)
1272
get_arg(arg, sizeof(arg), &p);
1273
if (av_strcasecmp(arg, "allow") == 0)
1274
acl.action = IP_ALLOW;
1275
else if (av_strcasecmp(arg, "deny") == 0)
1276
acl.action = IP_DENY;
1278
fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
1279
filename, line_num, arg);
1283
get_arg(arg, sizeof(arg), &p);
1285
if (resolve_host(&acl.first, arg) != 0) {
1286
fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
1287
filename, line_num, arg);
1290
acl.last = acl.first;
1292
get_arg(arg, sizeof(arg), &p);
1295
if (resolve_host(&acl.last, arg) != 0) {
1296
fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
1297
filename, line_num, arg);
1303
IPAddressACL *nacl = av_mallocz(sizeof(*nacl));
1304
IPAddressACL **naclp = 0;
1310
naclp = &stream->acl;
1316
fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
1317
filename, line_num);
1323
naclp = &(*naclp)->next;
1331
static IPAddressACL* parse_dynamic_acl(FFStream *stream, HTTPContext *c)
1336
IPAddressACL *acl = NULL;
1340
f = fopen(stream->dynamic_acl, "r");
1342
perror(stream->dynamic_acl);
1346
acl = av_mallocz(sizeof(IPAddressACL));
1350
if (fgets(line, sizeof(line), f) == NULL)
1356
if (*p == '\0' || *p == '#')
1358
get_arg(cmd, sizeof(cmd), &p);
1360
if (!av_strcasecmp(cmd, "ACL"))
1361
parse_acl_row(NULL, NULL, acl, p, stream->dynamic_acl, line_num);
1368
static void free_acl_list(IPAddressACL *in_acl)
1370
IPAddressACL *pacl,*pacl2;
1380
static int validate_acl_list(IPAddressACL *in_acl, HTTPContext *c)
1382
enum IPAddressAction last_action = IP_DENY;
1384
struct in_addr *src = &c->from_addr.sin_addr;
1385
unsigned long src_addr = src->s_addr;
1387
for (acl = in_acl; acl; acl = acl->next) {
1388
if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr)
1389
return (acl->action == IP_ALLOW) ? 1 : 0;
1390
last_action = acl->action;
1393
/* Nothing matched, so return not the last action */
1394
return (last_action == IP_DENY) ? 1 : 0;
1397
static int validate_acl(FFStream *stream, HTTPContext *c)
1403
/* if stream->acl is null validate_acl_list will return 1 */
1404
ret = validate_acl_list(stream->acl, c);
1406
if (stream->dynamic_acl[0]) {
1407
acl = parse_dynamic_acl(stream, c);
1409
ret = validate_acl_list(acl, c);
1417
/* compute the real filename of a file by matching it without its
1418
extensions to all the stream filenames */
1419
static void compute_real_filename(char *filename, int max_size)
1426
/* compute filename by matching without the file extensions */
1427
av_strlcpy(file1, filename, sizeof(file1));
1428
p = strrchr(file1, '.');
1431
for(stream = first_stream; stream != NULL; stream = stream->next) {
1432
av_strlcpy(file2, stream->filename, sizeof(file2));
1433
p = strrchr(file2, '.');
1436
if (!strcmp(file1, file2)) {
1437
av_strlcpy(filename, stream->filename, max_size);
1452
/* parse http request and prepare header */
1453
static int http_parse_request(HTTPContext *c)
1456
enum RedirType redir_type;
1458
char info[1024], filename[1024];
1462
const char *mime_type;
1466
char *useragent = 0;
1469
get_word(cmd, sizeof(cmd), (const char **)&p);
1470
av_strlcpy(c->method, cmd, sizeof(c->method));
1472
if (!strcmp(cmd, "GET"))
1474
else if (!strcmp(cmd, "POST"))
1479
get_word(url, sizeof(url), (const char **)&p);
1480
av_strlcpy(c->url, url, sizeof(c->url));
1482
get_word(protocol, sizeof(protocol), (const char **)&p);
1483
if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
1486
av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
1489
http_log("%s - - New connection: %s %s\n", inet_ntoa(c->from_addr.sin_addr), cmd, url);
1491
/* find the filename and the optional info string in the request */
1492
p = strchr(url, '?');
1494
av_strlcpy(info, p, sizeof(info));
1499
av_strlcpy(filename, url + ((*url == '/') ? 1 : 0), sizeof(filename)-1);
1501
for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1502
if (av_strncasecmp(p, "User-Agent:", 11) == 0) {
1504
if (*useragent && *useragent != '\n' && isspace(*useragent))
1508
p = strchr(p, '\n');
1515
redir_type = REDIR_NONE;
1516
if (av_match_ext(filename, "asx")) {
1517
redir_type = REDIR_ASX;
1518
filename[strlen(filename)-1] = 'f';
1519
} else if (av_match_ext(filename, "asf") &&
1520
(!useragent || av_strncasecmp(useragent, "NSPlayer", 8) != 0)) {
1521
/* if this isn't WMP or lookalike, return the redirector file */
1522
redir_type = REDIR_ASF;
1523
} else if (av_match_ext(filename, "rpm,ram")) {
1524
redir_type = REDIR_RAM;
1525
strcpy(filename + strlen(filename)-2, "m");
1526
} else if (av_match_ext(filename, "rtsp")) {
1527
redir_type = REDIR_RTSP;
1528
compute_real_filename(filename, sizeof(filename) - 1);
1529
} else if (av_match_ext(filename, "sdp")) {
1530
redir_type = REDIR_SDP;
1531
compute_real_filename(filename, sizeof(filename) - 1);
1534
// "redirect" / request to index.html
1535
if (!strlen(filename))
1536
av_strlcpy(filename, "index.html", sizeof(filename) - 1);
1538
stream = first_stream;
1539
while (stream != NULL) {
1540
if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
1542
stream = stream->next;
1544
if (stream == NULL) {
1545
snprintf(msg, sizeof(msg), "File '%s' not found", url);
1546
http_log("File '%s' not found\n", url);
1551
memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
1552
memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
1554
if (stream->stream_type == STREAM_TYPE_REDIRECT) {
1555
c->http_error = 301;
1557
q += snprintf(q, c->buffer_size,
1558
"HTTP/1.0 301 Moved\r\n"
1560
"Content-type: text/html\r\n"
1562
"<html><head><title>Moved</title></head><body>\r\n"
1563
"You should be <a href=\"%s\">redirected</a>.\r\n"
1564
"</body></html>\r\n", stream->feed_filename, stream->feed_filename);
1565
/* prepare output buffer */
1566
c->buffer_ptr = c->buffer;
1568
c->state = HTTPSTATE_SEND_HEADER;
1572
/* If this is WMP, get the rate information */
1573
if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1574
if (modify_current_stream(c, ratebuf)) {
1575
for (i = 0; i < FF_ARRAY_ELEMS(c->feed_streams); i++) {
1576
if (c->switch_feed_streams[i] >= 0)
1577
c->switch_feed_streams[i] = -1;
1582
if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE)
1583
current_bandwidth += stream->bandwidth;
1585
/* If already streaming this feed, do not let start another feeder. */
1586
if (stream->feed_opened) {
1587
snprintf(msg, sizeof(msg), "This feed is already being received.");
1588
http_log("Feed '%s' already being received\n", stream->feed_filename);
1592
if (c->post == 0 && max_bandwidth < current_bandwidth) {
1593
c->http_error = 503;
1595
q += snprintf(q, c->buffer_size,
1596
"HTTP/1.0 503 Server too busy\r\n"
1597
"Content-type: text/html\r\n"
1599
"<html><head><title>Too busy</title></head><body>\r\n"
1600
"<p>The server is too busy to serve your request at this time.</p>\r\n"
1601
"<p>The bandwidth being served (including your stream) is %"PRIu64"kbit/sec, "
1602
"and this exceeds the limit of %"PRIu64"kbit/sec.</p>\r\n"
1603
"</body></html>\r\n", current_bandwidth, max_bandwidth);
1604
/* prepare output buffer */
1605
c->buffer_ptr = c->buffer;
1607
c->state = HTTPSTATE_SEND_HEADER;
1611
if (redir_type != REDIR_NONE) {
1614
for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1615
if (av_strncasecmp(p, "Host:", 5) == 0) {
1619
p = strchr(p, '\n');
1630
while (isspace(*hostinfo))
1633
eoh = strchr(hostinfo, '\n');
1635
if (eoh[-1] == '\r')
1638
if (eoh - hostinfo < sizeof(hostbuf) - 1) {
1639
memcpy(hostbuf, hostinfo, eoh - hostinfo);
1640
hostbuf[eoh - hostinfo] = 0;
1642
c->http_error = 200;
1644
switch(redir_type) {
1646
q += snprintf(q, c->buffer_size,
1647
"HTTP/1.0 200 ASX Follows\r\n"
1648
"Content-type: video/x-ms-asf\r\n"
1650
"<ASX Version=\"3\">\r\n"
1651
//"<!-- Autogenerated by avserver -->\r\n"
1652
"<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n"
1653
"</ASX>\r\n", hostbuf, filename, info);
1656
q += snprintf(q, c->buffer_size,
1657
"HTTP/1.0 200 RAM Follows\r\n"
1658
"Content-type: audio/x-pn-realaudio\r\n"
1660
"# Autogenerated by avserver\r\n"
1661
"http://%s/%s%s\r\n", hostbuf, filename, info);
1664
q += snprintf(q, c->buffer_size,
1665
"HTTP/1.0 200 ASF Redirect follows\r\n"
1666
"Content-type: video/x-ms-asf\r\n"
1669
"Ref1=http://%s/%s%s\r\n", hostbuf, filename, info);
1673
char hostname[256], *p;
1674
/* extract only hostname */
1675
av_strlcpy(hostname, hostbuf, sizeof(hostname));
1676
p = strrchr(hostname, ':');
1679
q += snprintf(q, c->buffer_size,
1680
"HTTP/1.0 200 RTSP Redirect follows\r\n"
1681
/* XXX: incorrect mime type ? */
1682
"Content-type: application/x-rtsp\r\n"
1684
"rtsp://%s:%d/%s\r\n", hostname, ntohs(my_rtsp_addr.sin_port), filename);
1690
int sdp_data_size, len;
1691
struct sockaddr_in my_addr;
1693
q += snprintf(q, c->buffer_size,
1694
"HTTP/1.0 200 OK\r\n"
1695
"Content-type: application/sdp\r\n"
1698
len = sizeof(my_addr);
1699
getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
1701
/* XXX: should use a dynamic buffer */
1702
sdp_data_size = prepare_sdp_description(stream,
1705
if (sdp_data_size > 0) {
1706
memcpy(q, sdp_data, sdp_data_size);
1718
/* prepare output buffer */
1719
c->buffer_ptr = c->buffer;
1721
c->state = HTTPSTATE_SEND_HEADER;
1727
snprintf(msg, sizeof(msg), "ASX/RAM file not handled");
1731
stream->conns_served++;
1733
/* XXX: add there authenticate and IP match */
1736
/* if post, it means a feed is being sent */
1737
if (!stream->is_feed) {
1738
/* However it might be a status report from WMP! Let us log the
1739
* data as it might come in handy one day. */
1743
for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1744
if (av_strncasecmp(p, "Pragma: log-line=", 17) == 0) {
1748
if (av_strncasecmp(p, "Pragma: client-id=", 18) == 0)
1749
client_id = strtol(p + 18, 0, 10);
1750
p = strchr(p, '\n');
1758
char *eol = strchr(logline, '\n');
1763
if (eol[-1] == '\r')
1765
http_log("%.*s\n", (int) (eol - logline), logline);
1766
c->suppress_log = 1;
1771
http_log("\nGot request:\n%s\n", c->buffer);
1774
if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1777
/* Now we have to find the client_id */
1778
for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
1779
if (wmpc->wmp_client_id == client_id)
1783
if (wmpc && modify_current_stream(wmpc, ratebuf))
1784
wmpc->switch_pending = 1;
1787
snprintf(msg, sizeof(msg), "POST command not handled");
1791
if (http_start_receive_data(c) < 0) {
1792
snprintf(msg, sizeof(msg), "could not open feed");
1796
c->state = HTTPSTATE_RECEIVE_DATA;
1801
if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0)
1802
http_log("\nGot request:\n%s\n", c->buffer);
1805
if (c->stream->stream_type == STREAM_TYPE_STATUS)
1808
/* open input stream */
1809
if (open_input_stream(c, info) < 0) {
1810
snprintf(msg, sizeof(msg), "Input stream corresponding to '%s' not found", url);
1814
/* prepare http header */
1816
q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1817
mime_type = c->stream->fmt->mime_type;
1819
mime_type = "application/x-octet-stream";
1820
q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Pragma: no-cache\r\n");
1822
/* for asf, we need extra headers */
1823
if (!strcmp(c->stream->fmt->name,"asf_stream")) {
1824
/* Need to allocate a client id */
1826
c->wmp_client_id = av_lfg_get(&random_state);
1828
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);
1830
q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-Type: %s\r\n", mime_type);
1831
q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1833
/* prepare output buffer */
1835
c->buffer_ptr = c->buffer;
1837
c->state = HTTPSTATE_SEND_HEADER;
1840
c->http_error = 404;
1842
q += snprintf(q, c->buffer_size,
1843
"HTTP/1.0 404 Not Found\r\n"
1844
"Content-type: text/html\r\n"
1847
"<head><title>404 Not Found</title></head>\n"
1850
/* prepare output buffer */
1851
c->buffer_ptr = c->buffer;
1853
c->state = HTTPSTATE_SEND_HEADER;
1857
c->http_error = 200; /* horrible : we use this value to avoid
1858
going to the send data state */
1859
c->state = HTTPSTATE_SEND_HEADER;
1863
static void fmt_bytecount(AVIOContext *pb, int64_t count)
1865
static const char *suffix = " kMGTP";
1868
for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++);
1870
avio_printf(pb, "%"PRId64"%c", count, *s);
1873
static void compute_status(HTTPContext *c)
1882
if (avio_open_dyn_buf(&pb) < 0) {
1883
/* XXX: return an error ? */
1884
c->buffer_ptr = c->buffer;
1885
c->buffer_end = c->buffer;
1889
avio_printf(pb, "HTTP/1.0 200 OK\r\n");
1890
avio_printf(pb, "Content-type: %s\r\n", "text/html");
1891
avio_printf(pb, "Pragma: no-cache\r\n");
1892
avio_printf(pb, "\r\n");
1894
avio_printf(pb, "<html><head><title>%s Status</title>\n", program_name);
1895
if (c->stream->feed_filename[0])
1896
avio_printf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
1897
avio_printf(pb, "</head>\n<body>");
1898
avio_printf(pb, "<h1>%s Status</h1>\n", program_name);
1900
avio_printf(pb, "<h2>Available Streams</h2>\n");
1901
avio_printf(pb, "<table cellspacing=0 cellpadding=4>\n");
1902
avio_printf(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");
1903
stream = first_stream;
1904
while (stream != NULL) {
1905
char sfilename[1024];
1908
if (stream->feed != stream) {
1909
av_strlcpy(sfilename, stream->filename, sizeof(sfilename) - 10);
1910
eosf = sfilename + strlen(sfilename);
1911
if (eosf - sfilename >= 4) {
1912
if (strcmp(eosf - 4, ".asf") == 0)
1913
strcpy(eosf - 4, ".asx");
1914
else if (strcmp(eosf - 3, ".rm") == 0)
1915
strcpy(eosf - 3, ".ram");
1916
else if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
1917
/* generate a sample RTSP director if
1918
unicast. Generate an SDP redirector if
1920
eosf = strrchr(sfilename, '.');
1922
eosf = sfilename + strlen(sfilename);
1923
if (stream->is_multicast)
1924
strcpy(eosf, ".sdp");
1926
strcpy(eosf, ".rtsp");
1930
avio_printf(pb, "<tr><td><a href=\"/%s\">%s</a> ",
1931
sfilename, stream->filename);
1932
avio_printf(pb, "<td align=right> %d <td align=right> ",
1933
stream->conns_served);
1934
fmt_bytecount(pb, stream->bytes_served);
1935
switch(stream->stream_type) {
1936
case STREAM_TYPE_LIVE: {
1937
int audio_bit_rate = 0;
1938
int video_bit_rate = 0;
1939
const char *audio_codec_name = "";
1940
const char *video_codec_name = "";
1941
const char *audio_codec_name_extra = "";
1942
const char *video_codec_name_extra = "";
1944
for(i=0;i<stream->nb_streams;i++) {
1945
AVStream *st = stream->streams[i];
1946
AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1947
switch(st->codec->codec_type) {
1948
case AVMEDIA_TYPE_AUDIO:
1949
audio_bit_rate += st->codec->bit_rate;
1951
if (*audio_codec_name)
1952
audio_codec_name_extra = "...";
1953
audio_codec_name = codec->name;
1956
case AVMEDIA_TYPE_VIDEO:
1957
video_bit_rate += st->codec->bit_rate;
1959
if (*video_codec_name)
1960
video_codec_name_extra = "...";
1961
video_codec_name = codec->name;
1964
case AVMEDIA_TYPE_DATA:
1965
video_bit_rate += st->codec->bit_rate;
1971
avio_printf(pb, "<td align=center> %s <td align=right> %d <td align=right> %d <td> %s %s <td align=right> %d <td> %s %s",
1974
video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
1975
audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
1977
avio_printf(pb, "<td>%s", stream->feed->filename);
1979
avio_printf(pb, "<td>%s", stream->feed_filename);
1980
avio_printf(pb, "\n");
1984
avio_printf(pb, "<td align=center> - <td align=right> - <td align=right> - <td><td align=right> - <td>\n");
1988
stream = stream->next;
1990
avio_printf(pb, "</table>\n");
1992
stream = first_stream;
1993
while (stream != NULL) {
1994
if (stream->feed == stream) {
1995
avio_printf(pb, "<h2>Feed %s</h2>", stream->filename);
1997
avio_printf(pb, "Running as pid %d.\n", stream->pid);
1999
#if defined(linux) && !defined(CONFIG_NOCUTILS)
2004
/* This is somewhat linux specific I guess */
2005
snprintf(ps_cmd, sizeof(ps_cmd),
2006
"ps -o \"%%cpu,cputime\" --no-headers %d",
2009
pid_stat = popen(ps_cmd, "r");
2014
if (fscanf(pid_stat, "%10s %64s", cpuperc,
2016
avio_printf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
2024
avio_printf(pb, "<p>");
2026
avio_printf(pb, "<table cellspacing=0 cellpadding=4><tr><th>Stream<th>type<th>kbits/s<th align=left>codec<th align=left>Parameters\n");
2028
for (i = 0; i < stream->nb_streams; i++) {
2029
AVStream *st = stream->streams[i];
2030
AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
2031
const char *type = "unknown";
2032
char parameters[64];
2036
switch(st->codec->codec_type) {
2037
case AVMEDIA_TYPE_AUDIO:
2039
snprintf(parameters, sizeof(parameters), "%d channel(s), %d Hz", st->codec->channels, st->codec->sample_rate);
2041
case AVMEDIA_TYPE_VIDEO:
2043
snprintf(parameters, sizeof(parameters), "%dx%d, q=%d-%d, fps=%d", st->codec->width, st->codec->height,
2044
st->codec->qmin, st->codec->qmax, st->codec->time_base.den / st->codec->time_base.num);
2049
avio_printf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
2050
i, type, st->codec->bit_rate/1000, codec ? codec->name : "", parameters);
2052
avio_printf(pb, "</table>\n");
2055
stream = stream->next;
2058
/* connection status */
2059
avio_printf(pb, "<h2>Connection Status</h2>\n");
2061
avio_printf(pb, "Number of connections: %d / %d<br>\n",
2062
nb_connections, nb_max_connections);
2064
avio_printf(pb, "Bandwidth in use: %"PRIu64"k / %"PRIu64"k<br>\n",
2065
current_bandwidth, max_bandwidth);
2067
avio_printf(pb, "<table>\n");
2068
avio_printf(pb, "<tr><th>#<th>File<th>IP<th>Proto<th>State<th>Target bits/sec<th>Actual bits/sec<th>Bytes transferred\n");
2069
c1 = first_http_ctx;
2071
while (c1 != NULL) {
2077
for (j = 0; j < c1->stream->nb_streams; j++) {
2078
if (!c1->stream->feed)
2079
bitrate += c1->stream->streams[j]->codec->bit_rate;
2080
else if (c1->feed_streams[j] >= 0)
2081
bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec->bit_rate;
2086
p = inet_ntoa(c1->from_addr.sin_addr);
2087
avio_printf(pb, "<tr><td><b>%d</b><td>%s%s<td>%s<td>%s<td>%s<td align=right>",
2089
c1->stream ? c1->stream->filename : "",
2090
c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
2093
http_state[c1->state]);
2094
fmt_bytecount(pb, bitrate);
2095
avio_printf(pb, "<td align=right>");
2096
fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
2097
avio_printf(pb, "<td align=right>");
2098
fmt_bytecount(pb, c1->data_count);
2099
avio_printf(pb, "\n");
2102
avio_printf(pb, "</table>\n");
2107
avio_printf(pb, "<hr size=1 noshade>Generated at %s", p);
2108
avio_printf(pb, "</body>\n</html>\n");
2110
len = avio_close_dyn_buf(pb, &c->pb_buffer);
2111
c->buffer_ptr = c->pb_buffer;
2112
c->buffer_end = c->pb_buffer + len;
2115
/* check if the parser needs to be opened for stream i */
2116
static void open_parser(AVFormatContext *s, int i)
2118
AVStream *st = s->streams[i];
2121
if (!st->codec->codec) {
2122
codec = avcodec_find_decoder(st->codec->codec_id);
2123
if (codec && (codec->capabilities & CODEC_CAP_PARSE_ONLY)) {
2124
st->codec->parse_only = 1;
2125
if (avcodec_open2(st->codec, codec, NULL) < 0)
2126
st->codec->parse_only = 0;
2131
static int open_input_stream(HTTPContext *c, const char *info)
2134
char input_filename[1024];
2135
AVFormatContext *s = NULL;
2139
/* find file name */
2140
if (c->stream->feed) {
2141
strcpy(input_filename, c->stream->feed->feed_filename);
2142
/* compute position (absolute time) */
2143
if (av_find_info_tag(buf, sizeof(buf), "date", info)) {
2144
if ((ret = av_parse_time(&stream_pos, buf, 0)) < 0)
2146
} else if (av_find_info_tag(buf, sizeof(buf), "buffer", info)) {
2147
int prebuffer = strtol(buf, 0, 10);
2148
stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
2150
stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
2152
strcpy(input_filename, c->stream->feed_filename);
2153
/* compute position (relative time) */
2154
if (av_find_info_tag(buf, sizeof(buf), "date", info)) {
2155
if ((ret = av_parse_time(&stream_pos, buf, 1)) < 0)
2160
if (input_filename[0] == '\0')
2164
if ((ret = avformat_open_input(&s, input_filename, c->stream->ifmt, &c->stream->in_opts)) < 0) {
2165
http_log("could not open %s: %d\n", input_filename, ret);
2168
s->flags |= AVFMT_FLAG_GENPTS;
2170
if (strcmp(s->iformat->name, "ffm") && avformat_find_stream_info(c->fmt_in, NULL) < 0) {
2171
http_log("Could not find stream info '%s'\n", input_filename);
2172
avformat_close_input(&s);
2176
/* open each parser */
2177
for(i=0;i<s->nb_streams;i++)
2180
/* choose stream as clock source (we favorize video stream if
2181
present) for packet sending */
2182
c->pts_stream_index = 0;
2183
for(i=0;i<c->stream->nb_streams;i++) {
2184
if (c->pts_stream_index == 0 &&
2185
c->stream->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
2186
c->pts_stream_index = i;
2190
if (c->fmt_in->iformat->read_seek)
2191
av_seek_frame(c->fmt_in, -1, stream_pos, 0);
2192
/* set the start time (needed for maxtime and RTP packet timing) */
2193
c->start_time = cur_time;
2194
c->first_pts = AV_NOPTS_VALUE;
2198
/* return the server clock (in us) */
2199
static int64_t get_server_clock(HTTPContext *c)
2201
/* compute current pts value from system time */
2202
return (cur_time - c->start_time) * 1000;
2205
/* return the estimated time at which the current packet must be sent
2207
static int64_t get_packet_send_clock(HTTPContext *c)
2209
int bytes_left, bytes_sent, frame_bytes;
2211
frame_bytes = c->cur_frame_bytes;
2212
if (frame_bytes <= 0)
2215
bytes_left = c->buffer_end - c->buffer_ptr;
2216
bytes_sent = frame_bytes - bytes_left;
2217
return c->cur_pts + (c->cur_frame_duration * bytes_sent) / frame_bytes;
2222
static int http_prepare_data(HTTPContext *c)
2225
AVFormatContext *ctx;
2227
av_freep(&c->pb_buffer);
2229
case HTTPSTATE_SEND_DATA_HEADER:
2230
memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
2231
av_dict_set(&c->fmt_ctx.metadata, "author" , c->stream->author , 0);
2232
av_dict_set(&c->fmt_ctx.metadata, "comment" , c->stream->comment , 0);
2233
av_dict_set(&c->fmt_ctx.metadata, "copyright", c->stream->copyright, 0);
2234
av_dict_set(&c->fmt_ctx.metadata, "title" , c->stream->title , 0);
2236
c->fmt_ctx.streams = av_mallocz(sizeof(AVStream *) * c->stream->nb_streams);
2238
for(i=0;i<c->stream->nb_streams;i++) {
2240
c->fmt_ctx.streams[i] = av_mallocz(sizeof(AVStream));
2241
/* if file or feed, then just take streams from FFStream struct */
2242
if (!c->stream->feed ||
2243
c->stream->feed == c->stream)
2244
src = c->stream->streams[i];
2246
src = c->stream->feed->streams[c->stream->feed_streams[i]];
2248
*(c->fmt_ctx.streams[i]) = *src;
2249
c->fmt_ctx.streams[i]->priv_data = 0;
2250
c->fmt_ctx.streams[i]->codec->frame_number = 0; /* XXX: should be done in
2251
AVStream, not in codec */
2253
/* set output format parameters */
2254
c->fmt_ctx.oformat = c->stream->fmt;
2255
c->fmt_ctx.nb_streams = c->stream->nb_streams;
2257
c->got_key_frame = 0;
2259
/* prepare header and save header data in a stream */
2260
if (avio_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2261
/* XXX: potential leak */
2264
c->fmt_ctx.pb->seekable = 0;
2267
* HACK to avoid mpeg ps muxer to spit many underflow errors
2268
* Default value from Libav
2269
* Try to set it use configuration option
2271
c->fmt_ctx.preload = (int)(0.5*AV_TIME_BASE);
2272
c->fmt_ctx.max_delay = (int)(0.7*AV_TIME_BASE);
2274
if (avformat_write_header(&c->fmt_ctx, NULL) < 0) {
2275
http_log("Error writing output header\n");
2278
av_dict_free(&c->fmt_ctx.metadata);
2280
len = avio_close_dyn_buf(c->fmt_ctx.pb, &c->pb_buffer);
2281
c->buffer_ptr = c->pb_buffer;
2282
c->buffer_end = c->pb_buffer + len;
2284
c->state = HTTPSTATE_SEND_DATA;
2285
c->last_packet_sent = 0;
2287
case HTTPSTATE_SEND_DATA:
2288
/* find a new packet */
2289
/* read a packet from the input stream */
2290
if (c->stream->feed)
2291
ffm_set_write_index(c->fmt_in,
2292
c->stream->feed->feed_write_index,
2293
c->stream->feed->feed_size);
2295
if (c->stream->max_time &&
2296
c->stream->max_time + c->start_time - cur_time < 0)
2297
/* We have timed out */
2298
c->state = HTTPSTATE_SEND_DATA_TRAILER;
2302
ret = av_read_frame(c->fmt_in, &pkt);
2304
if (c->stream->feed) {
2305
/* if coming from feed, it means we reached the end of the
2306
ffm file, so must wait for more data */
2307
c->state = HTTPSTATE_WAIT_FEED;
2308
return 1; /* state changed */
2309
} else if (ret == AVERROR(EAGAIN)) {
2310
/* input not ready, come back later */
2313
if (c->stream->loop) {
2314
avformat_close_input(&c->fmt_in);
2315
if (open_input_stream(c, "") < 0)
2320
/* must send trailer now because eof or error */
2321
c->state = HTTPSTATE_SEND_DATA_TRAILER;
2325
int source_index = pkt.stream_index;
2326
/* update first pts if needed */
2327
if (c->first_pts == AV_NOPTS_VALUE) {
2328
c->first_pts = av_rescale_q(pkt.dts, c->fmt_in->streams[pkt.stream_index]->time_base, AV_TIME_BASE_Q);
2329
c->start_time = cur_time;
2331
/* send it to the appropriate stream */
2332
if (c->stream->feed) {
2333
/* if coming from a feed, select the right stream */
2334
if (c->switch_pending) {
2335
c->switch_pending = 0;
2336
for(i=0;i<c->stream->nb_streams;i++) {
2337
if (c->switch_feed_streams[i] == pkt.stream_index)
2338
if (pkt.flags & AV_PKT_FLAG_KEY)
2339
c->switch_feed_streams[i] = -1;
2340
if (c->switch_feed_streams[i] >= 0)
2341
c->switch_pending = 1;
2344
for(i=0;i<c->stream->nb_streams;i++) {
2345
if (c->stream->feed_streams[i] == pkt.stream_index) {
2346
AVStream *st = c->fmt_in->streams[source_index];
2347
pkt.stream_index = i;
2348
if (pkt.flags & AV_PKT_FLAG_KEY &&
2349
(st->codec->codec_type == AVMEDIA_TYPE_VIDEO ||
2350
c->stream->nb_streams == 1))
2351
c->got_key_frame = 1;
2352
if (!c->stream->send_on_key || c->got_key_frame)
2357
AVCodecContext *codec;
2358
AVStream *ist, *ost;
2360
ist = c->fmt_in->streams[source_index];
2361
/* specific handling for RTP: we use several
2362
output stream (one for each RTP
2363
connection). XXX: need more abstract handling */
2364
if (c->is_packetized) {
2365
/* compute send time and duration */
2366
c->cur_pts = av_rescale_q(pkt.dts, ist->time_base, AV_TIME_BASE_Q);
2367
c->cur_pts -= c->first_pts;
2368
c->cur_frame_duration = av_rescale_q(pkt.duration, ist->time_base, AV_TIME_BASE_Q);
2369
/* find RTP context */
2370
c->packet_stream_index = pkt.stream_index;
2371
ctx = c->rtp_ctx[c->packet_stream_index];
2373
av_free_packet(&pkt);
2376
codec = ctx->streams[0]->codec;
2377
/* only one stream per RTP connection */
2378
pkt.stream_index = 0;
2382
codec = ctx->streams[pkt.stream_index]->codec;
2385
if (c->is_packetized) {
2386
int max_packet_size;
2387
if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP)
2388
max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
2390
max_packet_size = url_get_max_packet_size(c->rtp_handles[c->packet_stream_index]);
2391
ret = ffio_open_dyn_packet_buf(&ctx->pb, max_packet_size);
2393
ret = avio_open_dyn_buf(&ctx->pb);
2396
/* XXX: potential leak */
2399
ost = ctx->streams[pkt.stream_index];
2401
ctx->pb->seekable = 0;
2402
if (pkt.dts != AV_NOPTS_VALUE)
2403
pkt.dts = av_rescale_q(pkt.dts, ist->time_base, ost->time_base);
2404
if (pkt.pts != AV_NOPTS_VALUE)
2405
pkt.pts = av_rescale_q(pkt.pts, ist->time_base, ost->time_base);
2406
pkt.duration = av_rescale_q(pkt.duration, ist->time_base, ost->time_base);
2407
if (av_write_frame(ctx, &pkt) < 0) {
2408
http_log("Error writing frame to output\n");
2409
c->state = HTTPSTATE_SEND_DATA_TRAILER;
2412
len = avio_close_dyn_buf(ctx->pb, &c->pb_buffer);
2413
c->cur_frame_bytes = len;
2414
c->buffer_ptr = c->pb_buffer;
2415
c->buffer_end = c->pb_buffer + len;
2417
codec->frame_number++;
2419
av_free_packet(&pkt);
2423
av_free_packet(&pkt);
2428
case HTTPSTATE_SEND_DATA_TRAILER:
2429
/* last packet test ? */
2430
if (c->last_packet_sent || c->is_packetized)
2433
/* prepare header */
2434
if (avio_open_dyn_buf(&ctx->pb) < 0) {
2435
/* XXX: potential leak */
2438
c->fmt_ctx.pb->seekable = 0;
2439
av_write_trailer(ctx);
2440
len = avio_close_dyn_buf(ctx->pb, &c->pb_buffer);
2441
c->buffer_ptr = c->pb_buffer;
2442
c->buffer_end = c->pb_buffer + len;
2444
c->last_packet_sent = 1;
2450
/* should convert the format at the same time */
2451
/* send data starting at c->buffer_ptr to the output connection
2452
(either UDP or TCP connection) */
2453
static int http_send_data(HTTPContext *c)
2458
if (c->buffer_ptr >= c->buffer_end) {
2459
ret = http_prepare_data(c);
2463
/* state change requested */
2466
if (c->is_packetized) {
2467
/* RTP data output */
2468
len = c->buffer_end - c->buffer_ptr;
2470
/* fail safe - should never happen */
2472
c->buffer_ptr = c->buffer_end;
2475
len = (c->buffer_ptr[0] << 24) |
2476
(c->buffer_ptr[1] << 16) |
2477
(c->buffer_ptr[2] << 8) |
2479
if (len > (c->buffer_end - c->buffer_ptr))
2481
if ((get_packet_send_clock(c) - get_server_clock(c)) > 0) {
2482
/* nothing to send yet: we can wait */
2486
c->data_count += len;
2487
update_datarate(&c->datarate, c->data_count);
2489
c->stream->bytes_served += len;
2491
if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP) {
2492
/* RTP packets are sent inside the RTSP TCP connection */
2494
int interleaved_index, size;
2496
HTTPContext *rtsp_c;
2499
/* if no RTSP connection left, error */
2502
/* if already sending something, then wait. */
2503
if (rtsp_c->state != RTSPSTATE_WAIT_REQUEST)
2505
if (avio_open_dyn_buf(&pb) < 0)
2507
interleaved_index = c->packet_stream_index * 2;
2508
/* RTCP packets are sent at odd indexes */
2509
if (c->buffer_ptr[1] == 200)
2510
interleaved_index++;
2511
/* write RTSP TCP header */
2513
header[1] = interleaved_index;
2514
header[2] = len >> 8;
2516
avio_write(pb, header, 4);
2517
/* write RTP packet data */
2519
avio_write(pb, c->buffer_ptr, len);
2520
size = avio_close_dyn_buf(pb, &c->packet_buffer);
2521
/* prepare asynchronous TCP sending */
2522
rtsp_c->packet_buffer_ptr = c->packet_buffer;
2523
rtsp_c->packet_buffer_end = c->packet_buffer + size;
2524
c->buffer_ptr += len;
2526
/* send everything we can NOW */
2527
len = send(rtsp_c->fd, rtsp_c->packet_buffer_ptr,
2528
rtsp_c->packet_buffer_end - rtsp_c->packet_buffer_ptr, 0);
2530
rtsp_c->packet_buffer_ptr += len;
2531
if (rtsp_c->packet_buffer_ptr < rtsp_c->packet_buffer_end) {
2532
/* if we could not send all the data, we will
2533
send it later, so a new state is needed to
2534
"lock" the RTSP TCP connection */
2535
rtsp_c->state = RTSPSTATE_SEND_PACKET;
2538
/* all data has been sent */
2539
av_freep(&c->packet_buffer);
2541
/* send RTP packet directly in UDP */
2543
url_write(c->rtp_handles[c->packet_stream_index],
2544
c->buffer_ptr, len);
2545
c->buffer_ptr += len;
2546
/* here we continue as we can send several packets per 10 ms slot */
2549
/* TCP data output */
2550
len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2552
if (ff_neterrno() != AVERROR(EAGAIN) &&
2553
ff_neterrno() != AVERROR(EINTR))
2554
/* error : close connection */
2559
c->buffer_ptr += len;
2561
c->data_count += len;
2562
update_datarate(&c->datarate, c->data_count);
2564
c->stream->bytes_served += len;
2572
static int http_start_receive_data(HTTPContext *c)
2576
if (c->stream->feed_opened)
2579
/* Don't permit writing to this one */
2580
if (c->stream->readonly)
2584
fd = open(c->stream->feed_filename, O_RDWR);
2586
http_log("Error opening feeder file: %s\n", strerror(errno));
2591
if (c->stream->truncate) {
2592
/* truncate feed file */
2593
ffm_write_write_index(c->feed_fd, FFM_PACKET_SIZE);
2594
ftruncate(c->feed_fd, FFM_PACKET_SIZE);
2595
http_log("Truncating feed file '%s'\n", c->stream->feed_filename);
2597
if ((c->stream->feed_write_index = ffm_read_write_index(fd)) < 0) {
2598
http_log("Error reading write index from feed file: %s\n", strerror(errno));
2603
c->stream->feed_write_index = FFMAX(ffm_read_write_index(fd), FFM_PACKET_SIZE);
2604
c->stream->feed_size = lseek(fd, 0, SEEK_END);
2605
lseek(fd, 0, SEEK_SET);
2607
/* init buffer input */
2608
c->buffer_ptr = c->buffer;
2609
c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2610
c->stream->feed_opened = 1;
2611
c->chunked_encoding = !!av_stristr(c->buffer, "Transfer-Encoding: chunked");
2615
static int http_receive_data(HTTPContext *c)
2618
int len, loop_run = 0;
2620
while (c->chunked_encoding && !c->chunk_size &&
2621
c->buffer_end > c->buffer_ptr) {
2622
/* read chunk header, if present */
2623
len = recv(c->fd, c->buffer_ptr, 1, 0);
2626
if (ff_neterrno() != AVERROR(EAGAIN) &&
2627
ff_neterrno() != AVERROR(EINTR))
2628
/* error : close connection */
2631
} else if (len == 0) {
2632
/* end of connection : close it */
2634
} else if (c->buffer_ptr - c->buffer >= 2 &&
2635
!memcmp(c->buffer_ptr - 1, "\r\n", 2)) {
2636
c->chunk_size = strtol(c->buffer, 0, 16);
2637
if (c->chunk_size == 0) // end of stream
2639
c->buffer_ptr = c->buffer;
2641
} else if (++loop_run > 10) {
2642
/* no chunk header, abort */
2649
if (c->buffer_end > c->buffer_ptr) {
2650
len = recv(c->fd, c->buffer_ptr,
2651
FFMIN(c->chunk_size, c->buffer_end - c->buffer_ptr), 0);
2653
if (ff_neterrno() != AVERROR(EAGAIN) &&
2654
ff_neterrno() != AVERROR(EINTR))
2655
/* error : close connection */
2657
} else if (len == 0)
2658
/* end of connection : close it */
2661
c->chunk_size -= len;
2662
c->buffer_ptr += len;
2663
c->data_count += len;
2664
update_datarate(&c->datarate, c->data_count);
2668
if (c->buffer_ptr - c->buffer >= 2 && c->data_count > FFM_PACKET_SIZE) {
2669
if (c->buffer[0] != 'f' ||
2670
c->buffer[1] != 'm') {
2671
http_log("Feed stream has become desynchronized -- disconnecting\n");
2676
if (c->buffer_ptr >= c->buffer_end) {
2677
FFStream *feed = c->stream;
2678
/* a packet has been received : write it in the store, except
2680
if (c->data_count > FFM_PACKET_SIZE) {
2682
// printf("writing pos=0x%"PRIx64" size=0x%"PRIx64"\n", feed->feed_write_index, feed->feed_size);
2683
/* XXX: use llseek or url_seek */
2684
lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2685
if (write(c->feed_fd, c->buffer, FFM_PACKET_SIZE) < 0) {
2686
http_log("Error writing to feed file: %s\n", strerror(errno));
2690
feed->feed_write_index += FFM_PACKET_SIZE;
2691
/* update file size */
2692
if (feed->feed_write_index > c->stream->feed_size)
2693
feed->feed_size = feed->feed_write_index;
2695
/* handle wrap around if max file size reached */
2696
if (c->stream->feed_max_size && feed->feed_write_index >= c->stream->feed_max_size)
2697
feed->feed_write_index = FFM_PACKET_SIZE;
2700
if (ffm_write_write_index(c->feed_fd, feed->feed_write_index) < 0) {
2701
http_log("Error writing index to feed file: %s\n", strerror(errno));
2705
/* wake up any waiting connections */
2706
for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2707
if (c1->state == HTTPSTATE_WAIT_FEED &&
2708
c1->stream->feed == c->stream->feed)
2709
c1->state = HTTPSTATE_SEND_DATA;
2712
/* We have a header in our hands that contains useful data */
2713
AVFormatContext *s = avformat_alloc_context();
2715
AVInputFormat *fmt_in;
2721
/* use feed output format name to find corresponding input format */
2722
fmt_in = av_find_input_format(feed->fmt->name);
2726
pb = avio_alloc_context(c->buffer, c->buffer_end - c->buffer,
2727
0, NULL, NULL, NULL, NULL);
2731
if (avformat_open_input(&s, c->stream->feed_filename, fmt_in, NULL) < 0) {
2736
/* Now we have the actual streams */
2737
if (s->nb_streams != feed->nb_streams) {
2738
avformat_close_input(&s);
2740
http_log("Feed '%s' stream number does not match registered feed\n",
2741
c->stream->feed_filename);
2745
for (i = 0; i < s->nb_streams; i++) {
2746
AVStream *fst = feed->streams[i];
2747
AVStream *st = s->streams[i];
2748
avcodec_copy_context(fst->codec, st->codec);
2751
avformat_close_input(&s);
2754
c->buffer_ptr = c->buffer;
2759
c->stream->feed_opened = 0;
2761
/* wake up any waiting connections to stop waiting for feed */
2762
for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2763
if (c1->state == HTTPSTATE_WAIT_FEED &&
2764
c1->stream->feed == c->stream->feed)
2765
c1->state = HTTPSTATE_SEND_DATA_TRAILER;
2770
/********************************************************************/
2773
static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2780
switch(error_number) {
2781
case RTSP_STATUS_OK:
2784
case RTSP_STATUS_METHOD:
2785
str = "Method Not Allowed";
2787
case RTSP_STATUS_BANDWIDTH:
2788
str = "Not Enough Bandwidth";
2790
case RTSP_STATUS_SESSION:
2791
str = "Session Not Found";
2793
case RTSP_STATUS_STATE:
2794
str = "Method Not Valid in This State";
2796
case RTSP_STATUS_AGGREGATE:
2797
str = "Aggregate operation not allowed";
2799
case RTSP_STATUS_ONLY_AGGREGATE:
2800
str = "Only aggregate operation allowed";
2802
case RTSP_STATUS_TRANSPORT:
2803
str = "Unsupported transport";
2805
case RTSP_STATUS_INTERNAL:
2806
str = "Internal Server Error";
2808
case RTSP_STATUS_SERVICE:
2809
str = "Service Unavailable";
2811
case RTSP_STATUS_VERSION:
2812
str = "RTSP Version not supported";
2815
str = "Unknown Error";
2819
avio_printf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
2820
avio_printf(c->pb, "CSeq: %d\r\n", c->seq);
2822
/* output GMT time */
2825
strftime(buf2, sizeof(buf2), "%a, %d %b %Y %H:%M:%S", tm);
2826
avio_printf(c->pb, "Date: %s GMT\r\n", buf2);
2829
static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
2831
rtsp_reply_header(c, error_number);
2832
avio_printf(c->pb, "\r\n");
2835
static int rtsp_parse_request(HTTPContext *c)
2837
const char *p, *p1, *p2;
2843
RTSPMessageHeader header1, *header = &header1;
2845
c->buffer_ptr[0] = '\0';
2848
get_word(cmd, sizeof(cmd), &p);
2849
get_word(url, sizeof(url), &p);
2850
get_word(protocol, sizeof(protocol), &p);
2852
av_strlcpy(c->method, cmd, sizeof(c->method));
2853
av_strlcpy(c->url, url, sizeof(c->url));
2854
av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
2856
if (avio_open_dyn_buf(&c->pb) < 0) {
2857
/* XXX: cannot do more */
2858
c->pb = NULL; /* safety */
2862
/* check version name */
2863
if (strcmp(protocol, "RTSP/1.0") != 0) {
2864
rtsp_reply_error(c, RTSP_STATUS_VERSION);
2868
/* parse each header line */
2869
memset(header, 0, sizeof(*header));
2870
/* skip to next line */
2871
while (*p != '\n' && *p != '\0')
2875
while (*p != '\0') {
2876
p1 = memchr(p, '\n', (char *)c->buffer_ptr - p);
2880
if (p2 > p && p2[-1] == '\r')
2882
/* skip empty line */
2886
if (len > sizeof(line) - 1)
2887
len = sizeof(line) - 1;
2888
memcpy(line, p, len);
2890
ff_rtsp_parse_line(header, line, NULL, NULL);
2894
/* handle sequence number */
2895
c->seq = header->seq;
2897
if (!strcmp(cmd, "DESCRIBE"))
2898
rtsp_cmd_describe(c, url);
2899
else if (!strcmp(cmd, "OPTIONS"))
2900
rtsp_cmd_options(c, url);
2901
else if (!strcmp(cmd, "SETUP"))
2902
rtsp_cmd_setup(c, url, header);
2903
else if (!strcmp(cmd, "PLAY"))
2904
rtsp_cmd_play(c, url, header);
2905
else if (!strcmp(cmd, "PAUSE"))
2906
rtsp_cmd_pause(c, url, header);
2907
else if (!strcmp(cmd, "TEARDOWN"))
2908
rtsp_cmd_teardown(c, url, header);
2910
rtsp_reply_error(c, RTSP_STATUS_METHOD);
2913
len = avio_close_dyn_buf(c->pb, &c->pb_buffer);
2914
c->pb = NULL; /* safety */
2916
/* XXX: cannot do more */
2919
c->buffer_ptr = c->pb_buffer;
2920
c->buffer_end = c->pb_buffer + len;
2921
c->state = RTSPSTATE_SEND_REPLY;
2925
static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
2926
struct in_addr my_ip)
2928
AVFormatContext *avc;
2929
AVStream *avs = NULL;
2932
avc = avformat_alloc_context();
2936
av_dict_set(&avc->metadata, "title",
2937
stream->title[0] ? stream->title : "No Title", 0);
2938
avc->nb_streams = stream->nb_streams;
2939
if (stream->is_multicast) {
2940
snprintf(avc->filename, 1024, "rtp://%s:%d?multicast=1?ttl=%d",
2941
inet_ntoa(stream->multicast_ip),
2942
stream->multicast_port, stream->multicast_ttl);
2944
snprintf(avc->filename, 1024, "rtp://0.0.0.0");
2947
if (avc->nb_streams >= INT_MAX/sizeof(*avc->streams) ||
2948
!(avc->streams = av_malloc(avc->nb_streams * sizeof(*avc->streams))))
2950
if (avc->nb_streams >= INT_MAX/sizeof(*avs) ||
2951
!(avs = av_malloc(avc->nb_streams * sizeof(*avs))))
2954
for(i = 0; i < stream->nb_streams; i++) {
2955
avc->streams[i] = &avs[i];
2956
avc->streams[i]->codec = stream->streams[i]->codec;
2958
*pbuffer = av_mallocz(2048);
2959
av_sdp_create(&avc, 1, *pbuffer, 2048);
2962
av_free(avc->streams);
2963
av_dict_free(&avc->metadata);
2967
return strlen(*pbuffer);
2970
static void rtsp_cmd_options(HTTPContext *c, const char *url)
2972
// rtsp_reply_header(c, RTSP_STATUS_OK);
2973
avio_printf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK");
2974
avio_printf(c->pb, "CSeq: %d\r\n", c->seq);
2975
avio_printf(c->pb, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
2976
avio_printf(c->pb, "\r\n");
2979
static void rtsp_cmd_describe(HTTPContext *c, const char *url)
2985
int content_length, len;
2986
struct sockaddr_in my_addr;
2988
/* find which url is asked */
2989
av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2994
for(stream = first_stream; stream != NULL; stream = stream->next) {
2995
if (!stream->is_feed &&
2996
stream->fmt && !strcmp(stream->fmt->name, "rtp") &&
2997
!strcmp(path, stream->filename)) {
3001
/* no stream found */
3002
rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
3006
/* prepare the media description in sdp format */
3008
/* get the host IP */
3009
len = sizeof(my_addr);
3010
getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
3011
content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
3012
if (content_length < 0) {
3013
rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
3016
rtsp_reply_header(c, RTSP_STATUS_OK);
3017
avio_printf(c->pb, "Content-Base: %s/\r\n", url);
3018
avio_printf(c->pb, "Content-Type: application/sdp\r\n");
3019
avio_printf(c->pb, "Content-Length: %d\r\n", content_length);
3020
avio_printf(c->pb, "\r\n");
3021
avio_write(c->pb, content, content_length);
3025
static HTTPContext *find_rtp_session(const char *session_id)
3029
if (session_id[0] == '\0')
3032
for(c = first_http_ctx; c != NULL; c = c->next) {
3033
if (!strcmp(c->session_id, session_id))
3039
static RTSPTransportField *find_transport(RTSPMessageHeader *h, enum RTSPLowerTransport lower_transport)
3041
RTSPTransportField *th;
3044
for(i=0;i<h->nb_transports;i++) {
3045
th = &h->transports[i];
3046
if (th->lower_transport == lower_transport)
3052
static void rtsp_cmd_setup(HTTPContext *c, const char *url,
3053
RTSPMessageHeader *h)
3056
int stream_index, rtp_port, rtcp_port;
3061
RTSPTransportField *th;
3062
struct sockaddr_in dest_addr;
3063
RTSPActionServerSetup setup;
3065
/* find which url is asked */
3066
av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
3071
/* now check each stream */
3072
for(stream = first_stream; stream != NULL; stream = stream->next) {
3073
if (!stream->is_feed &&
3074
stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
3075
/* accept aggregate filenames only if single stream */
3076
if (!strcmp(path, stream->filename)) {
3077
if (stream->nb_streams != 1) {
3078
rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
3085
for(stream_index = 0; stream_index < stream->nb_streams;
3087
snprintf(buf, sizeof(buf), "%s/streamid=%d",
3088
stream->filename, stream_index);
3089
if (!strcmp(path, buf))
3094
/* no stream found */
3095
rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
3099
/* generate session id if needed */
3100
if (h->session_id[0] == '\0')
3101
snprintf(h->session_id, sizeof(h->session_id), "%08x%08x",
3102
av_lfg_get(&random_state), av_lfg_get(&random_state));
3104
/* find rtp session, and create it if none found */
3105
rtp_c = find_rtp_session(h->session_id);
3107
/* always prefer UDP */
3108
th = find_transport(h, RTSP_LOWER_TRANSPORT_UDP);
3110
th = find_transport(h, RTSP_LOWER_TRANSPORT_TCP);
3112
rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
3117
rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id,
3118
th->lower_transport);
3120
rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
3124
/* open input stream */
3125
if (open_input_stream(rtp_c, "") < 0) {
3126
rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
3131
/* test if stream is OK (test needed because several SETUP needs
3132
to be done for a given file) */
3133
if (rtp_c->stream != stream) {
3134
rtsp_reply_error(c, RTSP_STATUS_SERVICE);
3138
/* test if stream is already set up */
3139
if (rtp_c->rtp_ctx[stream_index]) {
3140
rtsp_reply_error(c, RTSP_STATUS_STATE);
3144
/* check transport */
3145
th = find_transport(h, rtp_c->rtp_protocol);
3146
if (!th || (th->lower_transport == RTSP_LOWER_TRANSPORT_UDP &&
3147
th->client_port_min <= 0)) {
3148
rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
3152
/* setup default options */
3153
setup.transport_option[0] = '\0';
3154
dest_addr = rtp_c->from_addr;
3155
dest_addr.sin_port = htons(th->client_port_min);
3158
if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) {
3159
rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
3163
/* now everything is OK, so we can send the connection parameters */
3164
rtsp_reply_header(c, RTSP_STATUS_OK);
3166
avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3168
switch(rtp_c->rtp_protocol) {
3169
case RTSP_LOWER_TRANSPORT_UDP:
3170
rtp_port = ff_rtp_get_local_rtp_port(rtp_c->rtp_handles[stream_index]);
3171
rtcp_port = ff_rtp_get_local_rtcp_port(rtp_c->rtp_handles[stream_index]);
3172
avio_printf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
3173
"client_port=%d-%d;server_port=%d-%d",
3174
th->client_port_min, th->client_port_max,
3175
rtp_port, rtcp_port);
3177
case RTSP_LOWER_TRANSPORT_TCP:
3178
avio_printf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
3179
stream_index * 2, stream_index * 2 + 1);
3184
if (setup.transport_option[0] != '\0')
3185
avio_printf(c->pb, ";%s", setup.transport_option);
3186
avio_printf(c->pb, "\r\n");
3189
avio_printf(c->pb, "\r\n");
3193
/* find an rtp connection by using the session ID. Check consistency
3195
static HTTPContext *find_rtp_session_with_url(const char *url,
3196
const char *session_id)
3204
rtp_c = find_rtp_session(session_id);
3208
/* find which url is asked */
3209
av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
3213
if(!strcmp(path, rtp_c->stream->filename)) return rtp_c;
3214
for(s=0; s<rtp_c->stream->nb_streams; ++s) {
3215
snprintf(buf, sizeof(buf), "%s/streamid=%d",
3216
rtp_c->stream->filename, s);
3217
if(!strncmp(path, buf, sizeof(buf))) {
3218
// XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
3223
if (len > 0 && path[len - 1] == '/' &&
3224
!strncmp(path, rtp_c->stream->filename, len - 1))
3229
static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPMessageHeader *h)
3233
rtp_c = find_rtp_session_with_url(url, h->session_id);
3235
rtsp_reply_error(c, RTSP_STATUS_SESSION);
3239
if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3240
rtp_c->state != HTTPSTATE_WAIT_FEED &&
3241
rtp_c->state != HTTPSTATE_READY) {
3242
rtsp_reply_error(c, RTSP_STATUS_STATE);
3246
rtp_c->state = HTTPSTATE_SEND_DATA;
3248
/* now everything is OK, so we can send the connection parameters */
3249
rtsp_reply_header(c, RTSP_STATUS_OK);
3251
avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3252
avio_printf(c->pb, "\r\n");
3255
static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPMessageHeader *h)
3259
rtp_c = find_rtp_session_with_url(url, h->session_id);
3261
rtsp_reply_error(c, RTSP_STATUS_SESSION);
3265
if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3266
rtp_c->state != HTTPSTATE_WAIT_FEED) {
3267
rtsp_reply_error(c, RTSP_STATUS_STATE);
3271
rtp_c->state = HTTPSTATE_READY;
3272
rtp_c->first_pts = AV_NOPTS_VALUE;
3273
/* now everything is OK, so we can send the connection parameters */
3274
rtsp_reply_header(c, RTSP_STATUS_OK);
3276
avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3277
avio_printf(c->pb, "\r\n");
3280
static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPMessageHeader *h)
3284
rtp_c = find_rtp_session_with_url(url, h->session_id);
3286
rtsp_reply_error(c, RTSP_STATUS_SESSION);
3290
/* now everything is OK, so we can send the connection parameters */
3291
rtsp_reply_header(c, RTSP_STATUS_OK);
3293
avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3294
avio_printf(c->pb, "\r\n");
3296
/* abort the session */
3297
close_connection(rtp_c);
3301
/********************************************************************/
3304
static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
3305
FFStream *stream, const char *session_id,
3306
enum RTSPLowerTransport rtp_protocol)
3308
HTTPContext *c = NULL;
3309
const char *proto_str;
3311
/* XXX: should output a warning page when coming
3312
close to the connection limit */
3313
if (nb_connections >= nb_max_connections)
3316
/* add a new connection */
3317
c = av_mallocz(sizeof(HTTPContext));
3322
c->poll_entry = NULL;
3323
c->from_addr = *from_addr;
3324
c->buffer_size = IOBUFFER_INIT_SIZE;
3325
c->buffer = av_malloc(c->buffer_size);
3330
av_strlcpy(c->session_id, session_id, sizeof(c->session_id));
3331
c->state = HTTPSTATE_READY;
3332
c->is_packetized = 1;
3333
c->rtp_protocol = rtp_protocol;
3335
/* protocol is shown in statistics */
3336
switch(c->rtp_protocol) {
3337
case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
3338
proto_str = "MCAST";
3340
case RTSP_LOWER_TRANSPORT_UDP:
3343
case RTSP_LOWER_TRANSPORT_TCP:
3350
av_strlcpy(c->protocol, "RTP/", sizeof(c->protocol));
3351
av_strlcat(c->protocol, proto_str, sizeof(c->protocol));
3353
current_bandwidth += stream->bandwidth;
3355
c->next = first_http_ctx;
3367
/* add a new RTP stream in an RTP connection (used in RTSP SETUP
3368
command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
3370
static int rtp_new_av_stream(HTTPContext *c,
3371
int stream_index, struct sockaddr_in *dest_addr,
3372
HTTPContext *rtsp_c)
3374
AVFormatContext *ctx;
3377
URLContext *h = NULL;
3379
int max_packet_size;
3381
/* now we can open the relevant output stream */
3382
ctx = avformat_alloc_context();
3385
ctx->oformat = av_guess_format("rtp", NULL, NULL);
3387
st = av_mallocz(sizeof(AVStream));
3390
ctx->nb_streams = 1;
3391
ctx->streams = av_mallocz(sizeof(AVStream *) * ctx->nb_streams);
3394
ctx->streams[0] = st;
3396
if (!c->stream->feed ||
3397
c->stream->feed == c->stream)
3398
memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3401
c->stream->feed->streams[c->stream->feed_streams[stream_index]],
3403
st->priv_data = NULL;
3405
/* build destination RTP address */
3406
ipaddr = inet_ntoa(dest_addr->sin_addr);
3408
switch(c->rtp_protocol) {
3409
case RTSP_LOWER_TRANSPORT_UDP:
3410
case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
3413
/* XXX: also pass as parameter to function ? */
3414
if (c->stream->is_multicast) {
3416
ttl = c->stream->multicast_ttl;
3419
snprintf(ctx->filename, sizeof(ctx->filename),
3420
"rtp://%s:%d?multicast=1&ttl=%d",
3421
ipaddr, ntohs(dest_addr->sin_port), ttl);
3423
snprintf(ctx->filename, sizeof(ctx->filename),
3424
"rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
3427
if (url_open(&h, ctx->filename, AVIO_FLAG_WRITE) < 0)
3429
c->rtp_handles[stream_index] = h;
3430
max_packet_size = url_get_max_packet_size(h);
3432
case RTSP_LOWER_TRANSPORT_TCP:
3435
max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
3441
http_log("%s:%d - - \"PLAY %s/streamid=%d %s\"\n",
3442
ipaddr, ntohs(dest_addr->sin_port),
3443
c->stream->filename, stream_index, c->protocol);
3445
/* normally, no packets should be output here, but the packet size may be checked */
3446
if (ffio_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) {
3447
/* XXX: close stream */
3450
if (avformat_write_header(ctx, NULL) < 0) {
3457
avio_close_dyn_buf(ctx->pb, &dummy_buf);
3460
c->rtp_ctx[stream_index] = ctx;
3464
/********************************************************************/
3465
/* avserver initialization */
3467
static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec, int copy)
3471
fst = av_mallocz(sizeof(AVStream));
3475
fst->codec = avcodec_alloc_context3(NULL);
3476
memcpy(fst->codec, codec, sizeof(AVCodecContext));
3477
if (codec->extradata_size) {
3478
fst->codec->extradata = av_malloc(codec->extradata_size);
3479
memcpy(fst->codec->extradata, codec->extradata,
3480
codec->extradata_size);
3483
/* live streams must use the actual feed's codec since it may be
3484
* updated later to carry extradata needed by the streams.
3488
fst->priv_data = av_mallocz(sizeof(FeedData));
3489
fst->index = stream->nb_streams;
3490
av_set_pts_info(fst, 33, 1, 90000);
3491
fst->sample_aspect_ratio = codec->sample_aspect_ratio;
3492
stream->streams[stream->nb_streams++] = fst;
3496
/* return the stream number in the feed */
3497
static int add_av_stream(FFStream *feed, AVStream *st)
3500
AVCodecContext *av, *av1;
3504
for(i=0;i<feed->nb_streams;i++) {
3505
st = feed->streams[i];
3507
if (av1->codec_id == av->codec_id &&
3508
av1->codec_type == av->codec_type &&
3509
av1->bit_rate == av->bit_rate) {
3511
switch(av->codec_type) {
3512
case AVMEDIA_TYPE_AUDIO:
3513
if (av1->channels == av->channels &&
3514
av1->sample_rate == av->sample_rate)
3517
case AVMEDIA_TYPE_VIDEO:
3518
if (av1->width == av->width &&
3519
av1->height == av->height &&
3520
av1->time_base.den == av->time_base.den &&
3521
av1->time_base.num == av->time_base.num &&
3522
av1->gop_size == av->gop_size)
3531
fst = add_av_stream1(feed, av, 0);
3534
return feed->nb_streams - 1;
3537
static void remove_stream(FFStream *stream)
3541
while (*ps != NULL) {
3549
/* specific mpeg4 handling : we extract the raw parameters */
3550
static void extract_mpeg4_header(AVFormatContext *infile)
3552
int mpeg4_count, i, size;
3558
for(i=0;i<infile->nb_streams;i++) {
3559
st = infile->streams[i];
3560
if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3561
st->codec->extradata_size == 0) {
3568
printf("MPEG4 without extra data: trying to find header in %s\n", infile->filename);
3569
while (mpeg4_count > 0) {
3570
if (av_read_packet(infile, &pkt) < 0)
3572
st = infile->streams[pkt.stream_index];
3573
if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3574
st->codec->extradata_size == 0) {
3575
av_freep(&st->codec->extradata);
3576
/* fill extradata with the header */
3577
/* XXX: we make hard suppositions here ! */
3579
while (p < pkt.data + pkt.size - 4) {
3580
/* stop when vop header is found */
3581
if (p[0] == 0x00 && p[1] == 0x00 &&
3582
p[2] == 0x01 && p[3] == 0xb6) {
3583
size = p - pkt.data;
3584
// av_hex_dump_log(infile, AV_LOG_DEBUG, pkt.data, size);
3585
st->codec->extradata = av_malloc(size);
3586
st->codec->extradata_size = size;
3587
memcpy(st->codec->extradata, pkt.data, size);
3594
av_free_packet(&pkt);
3598
/* compute the needed AVStream for each file */
3599
static void build_file_streams(void)
3601
FFStream *stream, *stream_next;
3604
/* gather all streams */
3605
for(stream = first_stream; stream != NULL; stream = stream_next) {
3606
AVFormatContext *infile = NULL;
3607
stream_next = stream->next;
3608
if (stream->stream_type == STREAM_TYPE_LIVE &&
3610
/* the stream comes from a file */
3611
/* try to open the file */
3613
if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
3614
/* specific case : if transport stream output to RTP,
3615
we use a raw transport stream reader */
3616
av_dict_set(&stream->in_opts, "mpeg2ts_compute_pcr", "1", 0);
3619
http_log("Opening file '%s'\n", stream->feed_filename);
3620
if ((ret = avformat_open_input(&infile, stream->feed_filename, stream->ifmt, &stream->in_opts)) < 0) {
3621
http_log("Could not open '%s': %d\n", stream->feed_filename, ret);
3622
/* remove stream (no need to spend more time on it) */
3624
remove_stream(stream);
3626
/* find all the AVStreams inside and reference them in
3628
if (avformat_find_stream_info(infile, NULL) < 0) {
3629
http_log("Could not find codec parameters from '%s'\n",
3630
stream->feed_filename);
3631
avformat_close_input(&infile);
3634
extract_mpeg4_header(infile);
3636
for(i=0;i<infile->nb_streams;i++)
3637
add_av_stream1(stream, infile->streams[i]->codec, 1);
3639
avformat_close_input(&infile);
3645
/* compute the needed AVStream for each feed */
3646
static void build_feed_streams(void)
3648
FFStream *stream, *feed;
3651
/* gather all streams */
3652
for(stream = first_stream; stream != NULL; stream = stream->next) {
3653
feed = stream->feed;
3655
if (stream->is_feed) {
3656
for(i=0;i<stream->nb_streams;i++)
3657
stream->feed_streams[i] = i;
3659
/* we handle a stream coming from a feed */
3660
for(i=0;i<stream->nb_streams;i++)
3661
stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3666
/* create feed files if needed */
3667
for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
3670
if (avio_check(feed->feed_filename, AVIO_FLAG_READ) > 0) {
3671
/* See if it matches */
3672
AVFormatContext *s = NULL;
3675
if (avformat_open_input(&s, feed->feed_filename, NULL, NULL) >= 0) {
3676
/* Now see if it matches */
3677
if (s->nb_streams == feed->nb_streams) {
3679
for(i=0;i<s->nb_streams;i++) {
3681
sf = feed->streams[i];
3684
if (sf->index != ss->index ||
3686
http_log("Index & Id do not match for stream %d (%s)\n",
3687
i, feed->feed_filename);
3690
AVCodecContext *ccf, *ccs;
3694
#define CHECK_CODEC(x) (ccf->x != ccs->x)
3696
if (CHECK_CODEC(codec_id) || CHECK_CODEC(codec_type)) {
3697
http_log("Codecs do not match for stream %d\n", i);
3699
} else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3700
http_log("Codec bitrates do not match for stream %d\n", i);
3702
} else if (ccf->codec_type == AVMEDIA_TYPE_VIDEO) {
3703
if (CHECK_CODEC(time_base.den) ||
3704
CHECK_CODEC(time_base.num) ||
3705
CHECK_CODEC(width) ||
3706
CHECK_CODEC(height)) {
3707
http_log("Codec width, height and framerate do not match for stream %d\n", i);
3710
} else if (ccf->codec_type == AVMEDIA_TYPE_AUDIO) {
3711
if (CHECK_CODEC(sample_rate) ||
3712
CHECK_CODEC(channels) ||
3713
CHECK_CODEC(frame_size)) {
3714
http_log("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3718
http_log("Unknown codec type\n");
3726
http_log("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3727
feed->feed_filename, s->nb_streams, feed->nb_streams);
3729
avformat_close_input(&s);
3731
http_log("Deleting feed file '%s' as it appears to be corrupt\n",
3732
feed->feed_filename);
3735
if (feed->readonly) {
3736
http_log("Unable to delete feed file '%s' as it is marked readonly\n",
3737
feed->feed_filename);
3740
unlink(feed->feed_filename);
3743
if (avio_check(feed->feed_filename, AVIO_FLAG_WRITE) <= 0) {
3744
AVFormatContext s1 = {0}, *s = &s1;
3746
if (feed->readonly) {
3747
http_log("Unable to create feed file '%s' as it is marked readonly\n",
3748
feed->feed_filename);
3752
/* only write the header of the ffm file */
3753
if (avio_open(&s->pb, feed->feed_filename, AVIO_FLAG_WRITE) < 0) {
3754
http_log("Could not open output feed file '%s'\n",
3755
feed->feed_filename);
3758
s->oformat = feed->fmt;
3759
s->nb_streams = feed->nb_streams;
3760
s->streams = feed->streams;
3761
if (avformat_write_header(s, NULL) < 0) {
3762
http_log("Container doesn't supports the required parameters\n");
3765
/* XXX: need better api */
3766
av_freep(&s->priv_data);
3769
/* get feed size and write index */
3770
fd = open(feed->feed_filename, O_RDONLY);
3772
http_log("Could not open output feed file '%s'\n",
3773
feed->feed_filename);
3777
feed->feed_write_index = FFMAX(ffm_read_write_index(fd), FFM_PACKET_SIZE);
3778
feed->feed_size = lseek(fd, 0, SEEK_END);
3779
/* ensure that we do not wrap before the end of file */
3780
if (feed->feed_max_size && feed->feed_max_size < feed->feed_size)
3781
feed->feed_max_size = feed->feed_size;
3787
/* compute the bandwidth used by each stream */
3788
static void compute_bandwidth(void)
3794
for(stream = first_stream; stream != NULL; stream = stream->next) {
3796
for(i=0;i<stream->nb_streams;i++) {
3797
AVStream *st = stream->streams[i];
3798
switch(st->codec->codec_type) {
3799
case AVMEDIA_TYPE_AUDIO:
3800
case AVMEDIA_TYPE_VIDEO:
3801
bandwidth += st->codec->bit_rate;
3807
stream->bandwidth = (bandwidth + 999) / 1000;
3811
/* add a codec and set the default parameters */
3812
static void add_codec(FFStream *stream, AVCodecContext *av)
3816
/* compute default parameters */
3817
switch(av->codec_type) {
3818
case AVMEDIA_TYPE_AUDIO:
3819
if (av->bit_rate == 0)
3820
av->bit_rate = 64000;
3821
if (av->sample_rate == 0)
3822
av->sample_rate = 22050;
3823
if (av->channels == 0)
3826
case AVMEDIA_TYPE_VIDEO:
3827
if (av->bit_rate == 0)
3828
av->bit_rate = 64000;
3829
if (av->time_base.num == 0){
3830
av->time_base.den = 5;
3831
av->time_base.num = 1;
3833
if (av->width == 0 || av->height == 0) {
3837
/* Bitrate tolerance is less for streaming */
3838
if (av->bit_rate_tolerance == 0)
3839
av->bit_rate_tolerance = FFMAX(av->bit_rate / 4,
3840
(int64_t)av->bit_rate*av->time_base.num/av->time_base.den);
3845
if (av->max_qdiff == 0)
3847
av->qcompress = 0.5;
3850
if (!av->nsse_weight)
3851
av->nsse_weight = 8;
3853
av->frame_skip_cmp = FF_CMP_DCTMAX;
3855
av->me_method = ME_EPZS;
3856
av->rc_buffer_aggressivity = 1.0;
3859
av->rc_eq = "tex^qComp";
3860
if (!av->i_quant_factor)
3861
av->i_quant_factor = -0.8;
3862
if (!av->b_quant_factor)
3863
av->b_quant_factor = 1.25;
3864
if (!av->b_quant_offset)
3865
av->b_quant_offset = 1.25;
3866
if (!av->rc_max_rate)
3867
av->rc_max_rate = av->bit_rate * 2;
3869
if (av->rc_max_rate && !av->rc_buffer_size) {
3870
av->rc_buffer_size = av->rc_max_rate;
3879
st = av_mallocz(sizeof(AVStream));
3882
st->codec = avcodec_alloc_context3(NULL);
3883
stream->streams[stream->nb_streams++] = st;
3884
memcpy(st->codec, av, sizeof(AVCodecContext));
3887
static enum CodecID opt_audio_codec(const char *arg)
3889
AVCodec *p= avcodec_find_encoder_by_name(arg);
3891
if (p == NULL || p->type != AVMEDIA_TYPE_AUDIO)
3892
return CODEC_ID_NONE;
3897
static enum CodecID opt_video_codec(const char *arg)
3899
AVCodec *p= avcodec_find_encoder_by_name(arg);
3901
if (p == NULL || p->type != AVMEDIA_TYPE_VIDEO)
3902
return CODEC_ID_NONE;
3907
/* simplistic plugin support */
3910
static void load_module(const char *filename)
3913
void (*init_func)(void);
3914
dll = dlopen(filename, RTLD_NOW);
3916
fprintf(stderr, "Could not load module '%s' - %s\n",
3917
filename, dlerror());
3921
init_func = dlsym(dll, "avserver_module_init");
3924
"%s: init function 'avserver_module_init()' not found\n",
3933
static int avserver_opt_default(const char *opt, const char *arg,
3934
AVCodecContext *avctx, int type)
3937
const AVOption *o = av_opt_find(avctx, opt, NULL, type, 0);
3939
ret = av_opt_set(avctx, opt, arg, 0);
3943
static int avserver_opt_preset(const char *arg,
3944
AVCodecContext *avctx, int type,
3945
enum CodecID *audio_id, enum CodecID *video_id)
3948
char filename[1000], tmp[1000], tmp2[1000], line[1000];
3950
AVCodec *codec = avcodec_find_encoder(avctx->codec_id);
3952
if (!(f = get_preset_file(filename, sizeof(filename), arg, 0,
3953
codec ? codec->name : NULL))) {
3954
fprintf(stderr, "File for preset '%s' not found\n", arg);
3959
int e= fscanf(f, "%999[^\n]\n", line) - 1;
3960
if(line[0] == '#' && !e)
3962
e|= sscanf(line, "%999[^=]=%999[^\n]\n", tmp, tmp2) - 2;
3964
fprintf(stderr, "%s: Invalid syntax: '%s'\n", filename, line);
3968
if(!strcmp(tmp, "acodec")){
3969
*audio_id = opt_audio_codec(tmp2);
3970
}else if(!strcmp(tmp, "vcodec")){
3971
*video_id = opt_video_codec(tmp2);
3972
}else if(!strcmp(tmp, "scodec")){
3973
/* opt_subtitle_codec(tmp2); */
3974
}else if(avserver_opt_default(tmp, tmp2, avctx, type) < 0){
3975
fprintf(stderr, "%s: Invalid option or argument: '%s', parsed as '%s' = '%s'\n", filename, line, tmp, tmp2);
3986
static AVOutputFormat *avserver_guess_format(const char *short_name, const char *filename,
3987
const char *mime_type)
3989
AVOutputFormat *fmt = av_guess_format(short_name, filename, mime_type);
3992
AVOutputFormat *stream_fmt;
3993
char stream_format_name[64];
3995
snprintf(stream_format_name, sizeof(stream_format_name), "%s_stream", fmt->name);
3996
stream_fmt = av_guess_format(stream_format_name, NULL, NULL);
4005
static void report_config_error(const char *filename, int line_num, int *errors, const char *fmt, ...)
4009
fprintf(stderr, "%s:%d: ", filename, line_num);
4010
vfprintf(stderr, fmt, vl);
4016
static int parse_ffconfig(const char *filename)
4023
int val, errors, line_num;
4024
FFStream **last_stream, *stream, *redirect;
4025
FFStream **last_feed, *feed, *s;
4026
AVCodecContext audio_enc, video_enc;
4027
enum CodecID audio_id, video_id;
4029
f = fopen(filename, "r");
4037
first_stream = NULL;
4038
last_stream = &first_stream;
4040
last_feed = &first_feed;
4044
audio_id = CODEC_ID_NONE;
4045
video_id = CODEC_ID_NONE;
4047
#define ERROR(...) report_config_error(filename, line_num, &errors, __VA_ARGS__)
4049
if (fgets(line, sizeof(line), f) == NULL)
4055
if (*p == '\0' || *p == '#')
4058
get_arg(cmd, sizeof(cmd), &p);
4060
if (!av_strcasecmp(cmd, "Port")) {
4061
get_arg(arg, sizeof(arg), &p);
4063
if (val < 1 || val > 65536) {
4064
ERROR("Invalid_port: %s\n", arg);
4066
my_http_addr.sin_port = htons(val);
4067
} else if (!av_strcasecmp(cmd, "BindAddress")) {
4068
get_arg(arg, sizeof(arg), &p);
4069
if (resolve_host(&my_http_addr.sin_addr, arg) != 0) {
4070
ERROR("%s:%d: Invalid host/IP address: %s\n", arg);
4072
} else if (!av_strcasecmp(cmd, "NoDaemon")) {
4073
avserver_daemon = 0;
4074
} else if (!av_strcasecmp(cmd, "RTSPPort")) {
4075
get_arg(arg, sizeof(arg), &p);
4077
if (val < 1 || val > 65536) {
4078
ERROR("%s:%d: Invalid port: %s\n", arg);
4080
my_rtsp_addr.sin_port = htons(atoi(arg));
4081
} else if (!av_strcasecmp(cmd, "RTSPBindAddress")) {
4082
get_arg(arg, sizeof(arg), &p);
4083
if (resolve_host(&my_rtsp_addr.sin_addr, arg) != 0) {
4084
ERROR("Invalid host/IP address: %s\n", arg);
4086
} else if (!av_strcasecmp(cmd, "MaxHTTPConnections")) {
4087
get_arg(arg, sizeof(arg), &p);
4089
if (val < 1 || val > 65536) {
4090
ERROR("Invalid MaxHTTPConnections: %s\n", arg);
4092
nb_max_http_connections = val;
4093
} else if (!av_strcasecmp(cmd, "MaxClients")) {
4094
get_arg(arg, sizeof(arg), &p);
4096
if (val < 1 || val > nb_max_http_connections) {
4097
ERROR("Invalid MaxClients: %s\n", arg);
4099
nb_max_connections = val;
4101
} else if (!av_strcasecmp(cmd, "MaxBandwidth")) {
4103
get_arg(arg, sizeof(arg), &p);
4105
if (llval < 10 || llval > 10000000) {
4106
ERROR("Invalid MaxBandwidth: %s\n", arg);
4108
max_bandwidth = llval;
4109
} else if (!av_strcasecmp(cmd, "CustomLog")) {
4110
if (!avserver_debug)
4111
get_arg(logfilename, sizeof(logfilename), &p);
4112
} else if (!av_strcasecmp(cmd, "<Feed")) {
4113
/*********************************************/
4114
/* Feed related options */
4116
if (stream || feed) {
4117
ERROR("Already in a tag\n");
4119
feed = av_mallocz(sizeof(FFStream));
4120
get_arg(feed->filename, sizeof(feed->filename), &p);
4121
q = strrchr(feed->filename, '>');
4125
for (s = first_feed; s; s = s->next) {
4126
if (!strcmp(feed->filename, s->filename)) {
4127
ERROR("Feed '%s' already registered\n", s->filename);
4131
feed->fmt = av_guess_format("ffm", NULL, NULL);
4132
/* defaut feed file */
4133
snprintf(feed->feed_filename, sizeof(feed->feed_filename),
4134
"/tmp/%s.ffm", feed->filename);
4135
feed->feed_max_size = 5 * 1024 * 1024;
4137
feed->feed = feed; /* self feeding :-) */
4139
/* add in stream list */
4140
*last_stream = feed;
4141
last_stream = &feed->next;
4142
/* add in feed list */
4144
last_feed = &feed->next_feed;
4146
} else if (!av_strcasecmp(cmd, "Launch")) {
4150
feed->child_argv = av_mallocz(64 * sizeof(char *));
4152
for (i = 0; i < 62; i++) {
4153
get_arg(arg, sizeof(arg), &p);
4157
feed->child_argv[i] = av_strdup(arg);
4160
feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
4162
snprintf(feed->child_argv[i], 30+strlen(feed->filename),
4164
(my_http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" :
4165
inet_ntoa(my_http_addr.sin_addr),
4166
ntohs(my_http_addr.sin_port), feed->filename);
4168
} else if (!av_strcasecmp(cmd, "ReadOnlyFile")) {
4170
get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
4172
} else if (stream) {
4173
get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4175
} else if (!av_strcasecmp(cmd, "File")) {
4177
get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
4179
get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4180
} else if (!av_strcasecmp(cmd, "Truncate")) {
4182
get_arg(arg, sizeof(arg), &p);
4183
feed->truncate = strtod(arg, NULL);
4185
} else if (!av_strcasecmp(cmd, "FileMaxSize")) {
4190
get_arg(arg, sizeof(arg), &p);
4192
fsize = strtod(p1, &p1);
4193
switch(toupper(*p1)) {
4198
fsize *= 1024 * 1024;
4201
fsize *= 1024 * 1024 * 1024;
4204
feed->feed_max_size = (int64_t)fsize;
4205
if (feed->feed_max_size < FFM_PACKET_SIZE*4) {
4206
ERROR("Feed max file size is too small, must be at least %d\n", FFM_PACKET_SIZE*4);
4209
} else if (!av_strcasecmp(cmd, "</Feed>")) {
4211
ERROR("No corresponding <Feed> for </Feed>\n");
4214
} else if (!av_strcasecmp(cmd, "<Stream")) {
4215
/*********************************************/
4216
/* Stream related options */
4218
if (stream || feed) {
4219
ERROR("Already in a tag\n");
4222
stream = av_mallocz(sizeof(FFStream));
4223
get_arg(stream->filename, sizeof(stream->filename), &p);
4224
q = strrchr(stream->filename, '>');
4228
for (s = first_stream; s; s = s->next) {
4229
if (!strcmp(stream->filename, s->filename)) {
4230
ERROR("Stream '%s' already registered\n", s->filename);
4234
stream->fmt = avserver_guess_format(NULL, stream->filename, NULL);
4235
avcodec_get_context_defaults3(&video_enc, NULL);
4236
avcodec_get_context_defaults3(&audio_enc, NULL);
4237
audio_id = CODEC_ID_NONE;
4238
video_id = CODEC_ID_NONE;
4240
audio_id = stream->fmt->audio_codec;
4241
video_id = stream->fmt->video_codec;
4244
*last_stream = stream;
4245
last_stream = &stream->next;
4247
} else if (!av_strcasecmp(cmd, "Feed")) {
4248
get_arg(arg, sizeof(arg), &p);
4253
while (sfeed != NULL) {
4254
if (!strcmp(sfeed->filename, arg))
4256
sfeed = sfeed->next_feed;
4259
ERROR("feed '%s' not defined\n", arg);
4261
stream->feed = sfeed;
4263
} else if (!av_strcasecmp(cmd, "Format")) {
4264
get_arg(arg, sizeof(arg), &p);
4266
if (!strcmp(arg, "status")) {
4267
stream->stream_type = STREAM_TYPE_STATUS;
4270
stream->stream_type = STREAM_TYPE_LIVE;
4271
/* jpeg cannot be used here, so use single frame jpeg */
4272
if (!strcmp(arg, "jpeg"))
4273
strcpy(arg, "mjpeg");
4274
stream->fmt = avserver_guess_format(arg, NULL, NULL);
4276
ERROR("Unknown Format: %s\n", arg);
4280
audio_id = stream->fmt->audio_codec;
4281
video_id = stream->fmt->video_codec;
4284
} else if (!av_strcasecmp(cmd, "InputFormat")) {
4285
get_arg(arg, sizeof(arg), &p);
4287
stream->ifmt = av_find_input_format(arg);
4288
if (!stream->ifmt) {
4289
ERROR("Unknown input format: %s\n", arg);
4292
} else if (!av_strcasecmp(cmd, "FaviconURL")) {
4293
if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
4294
get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4296
ERROR("FaviconURL only permitted for status streams\n");
4298
} else if (!av_strcasecmp(cmd, "Author")) {
4300
get_arg(stream->author, sizeof(stream->author), &p);
4301
} else if (!av_strcasecmp(cmd, "Comment")) {
4303
get_arg(stream->comment, sizeof(stream->comment), &p);
4304
} else if (!av_strcasecmp(cmd, "Copyright")) {
4306
get_arg(stream->copyright, sizeof(stream->copyright), &p);
4307
} else if (!av_strcasecmp(cmd, "Title")) {
4309
get_arg(stream->title, sizeof(stream->title), &p);
4310
} else if (!av_strcasecmp(cmd, "Preroll")) {
4311
get_arg(arg, sizeof(arg), &p);
4313
stream->prebuffer = atof(arg) * 1000;
4314
} else if (!av_strcasecmp(cmd, "StartSendOnKey")) {
4316
stream->send_on_key = 1;
4317
} else if (!av_strcasecmp(cmd, "AudioCodec")) {
4318
get_arg(arg, sizeof(arg), &p);
4319
audio_id = opt_audio_codec(arg);
4320
if (audio_id == CODEC_ID_NONE) {
4321
ERROR("Unknown AudioCodec: %s\n", arg);
4323
} else if (!av_strcasecmp(cmd, "VideoCodec")) {
4324
get_arg(arg, sizeof(arg), &p);
4325
video_id = opt_video_codec(arg);
4326
if (video_id == CODEC_ID_NONE) {
4327
ERROR("Unknown VideoCodec: %s\n", arg);
4329
} else if (!av_strcasecmp(cmd, "MaxTime")) {
4330
get_arg(arg, sizeof(arg), &p);
4332
stream->max_time = atof(arg) * 1000;
4333
} else if (!av_strcasecmp(cmd, "AudioBitRate")) {
4334
get_arg(arg, sizeof(arg), &p);
4336
audio_enc.bit_rate = lrintf(atof(arg) * 1000);
4337
} else if (!av_strcasecmp(cmd, "AudioChannels")) {
4338
get_arg(arg, sizeof(arg), &p);
4340
audio_enc.channels = atoi(arg);
4341
} else if (!av_strcasecmp(cmd, "AudioSampleRate")) {
4342
get_arg(arg, sizeof(arg), &p);
4344
audio_enc.sample_rate = atoi(arg);
4345
} else if (!av_strcasecmp(cmd, "AudioQuality")) {
4346
get_arg(arg, sizeof(arg), &p);
4348
// audio_enc.quality = atof(arg) * 1000;
4350
} else if (!av_strcasecmp(cmd, "VideoBitRateRange")) {
4352
int minrate, maxrate;
4354
get_arg(arg, sizeof(arg), &p);
4356
if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
4357
video_enc.rc_min_rate = minrate * 1000;
4358
video_enc.rc_max_rate = maxrate * 1000;
4360
ERROR("Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n", arg);
4363
} else if (!av_strcasecmp(cmd, "Debug")) {
4365
get_arg(arg, sizeof(arg), &p);
4366
video_enc.debug = strtol(arg,0,0);
4368
} else if (!av_strcasecmp(cmd, "Strict")) {
4370
get_arg(arg, sizeof(arg), &p);
4371
video_enc.strict_std_compliance = atoi(arg);
4373
} else if (!av_strcasecmp(cmd, "VideoBufferSize")) {
4375
get_arg(arg, sizeof(arg), &p);
4376
video_enc.rc_buffer_size = atoi(arg) * 8*1024;
4378
} else if (!av_strcasecmp(cmd, "VideoBitRateTolerance")) {
4380
get_arg(arg, sizeof(arg), &p);
4381
video_enc.bit_rate_tolerance = atoi(arg) * 1000;
4383
} else if (!av_strcasecmp(cmd, "VideoBitRate")) {
4384
get_arg(arg, sizeof(arg), &p);
4386
video_enc.bit_rate = atoi(arg) * 1000;
4388
} else if (!av_strcasecmp(cmd, "VideoSize")) {
4389
get_arg(arg, sizeof(arg), &p);
4391
av_parse_video_size(&video_enc.width, &video_enc.height, arg);
4392
if ((video_enc.width % 16) != 0 ||
4393
(video_enc.height % 16) != 0) {
4394
ERROR("Image size must be a multiple of 16\n");
4397
} else if (!av_strcasecmp(cmd, "VideoFrameRate")) {
4398
get_arg(arg, sizeof(arg), &p);
4400
AVRational frame_rate;
4401
if (av_parse_video_rate(&frame_rate, arg) < 0) {
4402
ERROR("Incorrect frame rate: %s\n", arg);
4404
video_enc.time_base.num = frame_rate.den;
4405
video_enc.time_base.den = frame_rate.num;
4408
} else if (!av_strcasecmp(cmd, "VideoGopSize")) {
4409
get_arg(arg, sizeof(arg), &p);
4411
video_enc.gop_size = atoi(arg);
4412
} else if (!av_strcasecmp(cmd, "VideoIntraOnly")) {
4414
video_enc.gop_size = 1;
4415
} else if (!av_strcasecmp(cmd, "VideoHighQuality")) {
4417
video_enc.mb_decision = FF_MB_DECISION_BITS;
4418
} else if (!av_strcasecmp(cmd, "Video4MotionVector")) {
4420
video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove
4421
video_enc.flags |= CODEC_FLAG_4MV;
4423
} else if (!av_strcasecmp(cmd, "AVOptionVideo") ||
4424
!av_strcasecmp(cmd, "AVOptionAudio")) {
4426
AVCodecContext *avctx;
4428
get_arg(arg, sizeof(arg), &p);
4429
get_arg(arg2, sizeof(arg2), &p);
4430
if (!av_strcasecmp(cmd, "AVOptionVideo")) {
4432
type = AV_OPT_FLAG_VIDEO_PARAM;
4435
type = AV_OPT_FLAG_AUDIO_PARAM;
4437
if (avserver_opt_default(arg, arg2, avctx, type|AV_OPT_FLAG_ENCODING_PARAM)) {
4438
ERROR("AVOption error: %s %s\n", arg, arg2);
4440
} else if (!av_strcasecmp(cmd, "AVPresetVideo") ||
4441
!av_strcasecmp(cmd, "AVPresetAudio")) {
4442
AVCodecContext *avctx;
4444
get_arg(arg, sizeof(arg), &p);
4445
if (!av_strcasecmp(cmd, "AVPresetVideo")) {
4447
video_enc.codec_id = video_id;
4448
type = AV_OPT_FLAG_VIDEO_PARAM;
4451
audio_enc.codec_id = audio_id;
4452
type = AV_OPT_FLAG_AUDIO_PARAM;
4454
if (avserver_opt_preset(arg, avctx, type|AV_OPT_FLAG_ENCODING_PARAM, &audio_id, &video_id)) {
4455
ERROR("AVPreset error: %s\n", arg);
4457
} else if (!av_strcasecmp(cmd, "VideoTag")) {
4458
get_arg(arg, sizeof(arg), &p);
4459
if ((strlen(arg) == 4) && stream)
4460
video_enc.codec_tag = MKTAG(arg[0], arg[1], arg[2], arg[3]);
4461
} else if (!av_strcasecmp(cmd, "BitExact")) {
4463
video_enc.flags |= CODEC_FLAG_BITEXACT;
4464
} else if (!av_strcasecmp(cmd, "DctFastint")) {
4466
video_enc.dct_algo = FF_DCT_FASTINT;
4467
} else if (!av_strcasecmp(cmd, "IdctSimple")) {
4469
video_enc.idct_algo = FF_IDCT_SIMPLE;
4470
} else if (!av_strcasecmp(cmd, "Qscale")) {
4471
get_arg(arg, sizeof(arg), &p);
4473
video_enc.flags |= CODEC_FLAG_QSCALE;
4474
video_enc.global_quality = FF_QP2LAMBDA * atoi(arg);
4476
} else if (!av_strcasecmp(cmd, "VideoQDiff")) {
4477
get_arg(arg, sizeof(arg), &p);
4479
video_enc.max_qdiff = atoi(arg);
4480
if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
4481
ERROR("VideoQDiff out of range\n");
4484
} else if (!av_strcasecmp(cmd, "VideoQMax")) {
4485
get_arg(arg, sizeof(arg), &p);
4487
video_enc.qmax = atoi(arg);
4488
if (video_enc.qmax < 1 || video_enc.qmax > 31) {
4489
ERROR("VideoQMax out of range\n");
4492
} else if (!av_strcasecmp(cmd, "VideoQMin")) {
4493
get_arg(arg, sizeof(arg), &p);
4495
video_enc.qmin = atoi(arg);
4496
if (video_enc.qmin < 1 || video_enc.qmin > 31) {
4497
ERROR("VideoQMin out of range\n");
4500
} else if (!av_strcasecmp(cmd, "LumaElim")) {
4501
get_arg(arg, sizeof(arg), &p);
4503
video_enc.luma_elim_threshold = atoi(arg);
4504
} else if (!av_strcasecmp(cmd, "ChromaElim")) {
4505
get_arg(arg, sizeof(arg), &p);
4507
video_enc.chroma_elim_threshold = atoi(arg);
4508
} else if (!av_strcasecmp(cmd, "LumiMask")) {
4509
get_arg(arg, sizeof(arg), &p);
4511
video_enc.lumi_masking = atof(arg);
4512
} else if (!av_strcasecmp(cmd, "DarkMask")) {
4513
get_arg(arg, sizeof(arg), &p);
4515
video_enc.dark_masking = atof(arg);
4516
} else if (!av_strcasecmp(cmd, "NoVideo")) {
4517
video_id = CODEC_ID_NONE;
4518
} else if (!av_strcasecmp(cmd, "NoAudio")) {
4519
audio_id = CODEC_ID_NONE;
4520
} else if (!av_strcasecmp(cmd, "ACL")) {
4521
parse_acl_row(stream, feed, NULL, p, filename, line_num);
4522
} else if (!av_strcasecmp(cmd, "DynamicACL")) {
4524
get_arg(stream->dynamic_acl, sizeof(stream->dynamic_acl), &p);
4526
} else if (!av_strcasecmp(cmd, "RTSPOption")) {
4527
get_arg(arg, sizeof(arg), &p);
4529
av_freep(&stream->rtsp_option);
4530
stream->rtsp_option = av_strdup(arg);
4532
} else if (!av_strcasecmp(cmd, "MulticastAddress")) {
4533
get_arg(arg, sizeof(arg), &p);
4535
if (resolve_host(&stream->multicast_ip, arg) != 0) {
4536
ERROR("Invalid host/IP address: %s\n", arg);
4538
stream->is_multicast = 1;
4539
stream->loop = 1; /* default is looping */
4541
} else if (!av_strcasecmp(cmd, "MulticastPort")) {
4542
get_arg(arg, sizeof(arg), &p);
4544
stream->multicast_port = atoi(arg);
4545
} else if (!av_strcasecmp(cmd, "MulticastTTL")) {
4546
get_arg(arg, sizeof(arg), &p);
4548
stream->multicast_ttl = atoi(arg);
4549
} else if (!av_strcasecmp(cmd, "NoLoop")) {
4552
} else if (!av_strcasecmp(cmd, "</Stream>")) {
4554
ERROR("No corresponding <Stream> for </Stream>\n");
4556
if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
4557
if (audio_id != CODEC_ID_NONE) {
4558
audio_enc.codec_type = AVMEDIA_TYPE_AUDIO;
4559
audio_enc.codec_id = audio_id;
4560
add_codec(stream, &audio_enc);
4562
if (video_id != CODEC_ID_NONE) {
4563
video_enc.codec_type = AVMEDIA_TYPE_VIDEO;
4564
video_enc.codec_id = video_id;
4565
add_codec(stream, &video_enc);
4570
} else if (!av_strcasecmp(cmd, "<Redirect")) {
4571
/*********************************************/
4573
if (stream || feed || redirect) {
4574
ERROR("Already in a tag\n");
4576
redirect = av_mallocz(sizeof(FFStream));
4577
*last_stream = redirect;
4578
last_stream = &redirect->next;
4580
get_arg(redirect->filename, sizeof(redirect->filename), &p);
4581
q = strrchr(redirect->filename, '>');
4584
redirect->stream_type = STREAM_TYPE_REDIRECT;
4586
} else if (!av_strcasecmp(cmd, "URL")) {
4588
get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4589
} else if (!av_strcasecmp(cmd, "</Redirect>")) {
4591
ERROR("No corresponding <Redirect> for </Redirect>\n");
4593
if (!redirect->feed_filename[0]) {
4594
ERROR("No URL found for <Redirect>\n");
4598
} else if (!av_strcasecmp(cmd, "LoadModule")) {
4599
get_arg(arg, sizeof(arg), &p);
4603
ERROR("Module support not compiled into this version: '%s'\n", arg);
4606
ERROR("Incorrect keyword: '%s'\n", cmd);
4618
static void handle_child_exit(int sig)
4623
while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
4626
for (feed = first_feed; feed; feed = feed->next) {
4627
if (feed->pid == pid) {
4628
int uptime = time(0) - feed->pid_start;
4631
fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
4634
/* Turn off any more restarts */
4635
feed->child_argv = 0;
4640
need_to_start_children = 1;
4643
static void opt_debug(void)
4646
avserver_daemon = 0;
4647
logfilename[0] = '-';
4650
static void show_help(void)
4652
printf("usage: avserver [options]\n"
4653
"Hyper fast multi format Audio/Video streaming server\n");
4655
show_help_options(options, "Main options:\n", 0, 0);
4658
static const OptionDef options[] = {
4659
#include "cmdutils_common_opts.h"
4660
{ "n", OPT_BOOL, {(void *)&no_launch }, "enable no-launch mode" },
4661
{ "d", 0, {(void*)opt_debug}, "enable debug mode" },
4662
{ "f", HAS_ARG | OPT_STRING, {(void*)&config_filename }, "use configfile instead of /etc/avserver.conf", "configfile" },
4666
int main(int argc, char **argv)
4668
struct sigaction sigact;
4670
parse_loglevel(argc, argv, options);
4672
avformat_network_init();
4676
my_program_name = argv[0];
4677
my_program_dir = getcwd(0, 0);
4678
avserver_daemon = 1;
4680
parse_options(NULL, argc, argv, options, NULL);
4682
unsetenv("http_proxy"); /* Kill the http_proxy */
4684
av_lfg_init(&random_state, av_get_random_seed());
4686
memset(&sigact, 0, sizeof(sigact));
4687
sigact.sa_handler = handle_child_exit;
4688
sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
4689
sigaction(SIGCHLD, &sigact, 0);
4691
if (parse_ffconfig(config_filename) < 0) {
4692
fprintf(stderr, "Incorrect config file - exiting.\n");
4696
/* open log file if needed */
4697
if (logfilename[0] != '\0') {
4698
if (!strcmp(logfilename, "-"))
4701
logfile = fopen(logfilename, "a");
4702
av_log_set_callback(http_av_log);
4705
build_file_streams();
4707
build_feed_streams();
4709
compute_bandwidth();
4711
/* put the process in background and detach it from its TTY */
4712
if (avserver_daemon) {
4719
} else if (pid > 0) {
4726
open("/dev/null", O_RDWR);
4727
if (strcmp(logfilename, "-") != 0) {
4737
signal(SIGPIPE, SIG_IGN);
4739
if (avserver_daemon)
4742
if (http_server() < 0) {
4743
http_log("Could not start server\n");