It makes more sense to return 1 here in case of overflow.
[ffmpeg-lucabe.git] / ffserver.c
blobd17f9974128e521d514aee8554fa3c9a1988118a
1 /*
2 * Multiple format streaming server
3 * Copyright (c) 2000, 2001, 2002 Fabrice Bellard
5 * This file is part of FFmpeg.
7 * FFmpeg 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 * FFmpeg 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 FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 #include "config.h"
23 #ifndef HAVE_CLOSESOCKET
24 #define closesocket close
25 #endif
26 #include <string.h>
27 #include <stdlib.h>
28 #include "libavutil/random.h"
29 #include "libavutil/avstring.h"
30 #include "libavformat/avformat.h"
31 #include "libavformat/network.h"
32 #include "libavformat/os_support.h"
33 #include "libavformat/rtp.h"
34 #include "libavformat/rtsp.h"
35 #include "libavcodec/opt.h"
36 #include <stdarg.h>
37 #include <unistd.h>
38 #include <fcntl.h>
39 #include <sys/ioctl.h>
40 #ifdef HAVE_POLL_H
41 #include <poll.h>
42 #endif
43 #include <errno.h>
44 #include <sys/time.h>
45 #undef time //needed because HAVE_AV_CONFIG_H is defined on top
46 #include <time.h>
47 #include <sys/wait.h>
48 #include <signal.h>
49 #ifdef HAVE_DLFCN_H
50 #include <dlfcn.h>
51 #endif
53 #include "cmdutils.h"
55 #undef exit
57 const char program_name[] = "FFserver";
58 const int program_birth_year = 2000;
60 static const OptionDef options[];
62 /* maximum number of simultaneous HTTP connections */
63 #define HTTP_MAX_CONNECTIONS 2000
65 enum HTTPState {
66 HTTPSTATE_WAIT_REQUEST,
67 HTTPSTATE_SEND_HEADER,
68 HTTPSTATE_SEND_DATA_HEADER,
69 HTTPSTATE_SEND_DATA, /* sending TCP or UDP data */
70 HTTPSTATE_SEND_DATA_TRAILER,
71 HTTPSTATE_RECEIVE_DATA,
72 HTTPSTATE_WAIT_FEED, /* wait for data from the feed */
73 HTTPSTATE_READY,
75 RTSPSTATE_WAIT_REQUEST,
76 RTSPSTATE_SEND_REPLY,
77 RTSPSTATE_SEND_PACKET,
80 static const char *http_state[] = {
81 "HTTP_WAIT_REQUEST",
82 "HTTP_SEND_HEADER",
84 "SEND_DATA_HEADER",
85 "SEND_DATA",
86 "SEND_DATA_TRAILER",
87 "RECEIVE_DATA",
88 "WAIT_FEED",
89 "READY",
91 "RTSP_WAIT_REQUEST",
92 "RTSP_SEND_REPLY",
93 "RTSP_SEND_PACKET",
96 #define IOBUFFER_INIT_SIZE 8192
98 /* timeouts are in ms */
99 #define HTTP_REQUEST_TIMEOUT (15 * 1000)
100 #define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
102 #define SYNC_TIMEOUT (10 * 1000)
104 typedef struct {
105 int64_t count1, count2;
106 int64_t time1, time2;
107 } DataRateData;
109 /* context associated with one connection */
110 typedef struct HTTPContext {
111 enum HTTPState state;
112 int fd; /* socket file descriptor */
113 struct sockaddr_in from_addr; /* origin */
114 struct pollfd *poll_entry; /* used when polling */
115 int64_t timeout;
116 uint8_t *buffer_ptr, *buffer_end;
117 int http_error;
118 int post;
119 struct HTTPContext *next;
120 int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
121 int64_t data_count;
122 /* feed input */
123 int feed_fd;
124 /* input format handling */
125 AVFormatContext *fmt_in;
126 int64_t start_time; /* In milliseconds - this wraps fairly often */
127 int64_t first_pts; /* initial pts value */
128 int64_t cur_pts; /* current pts value from the stream in us */
129 int64_t cur_frame_duration; /* duration of the current frame in us */
130 int cur_frame_bytes; /* output frame size, needed to compute
131 the time at which we send each
132 packet */
133 int pts_stream_index; /* stream we choose as clock reference */
134 int64_t cur_clock; /* current clock reference value in us */
135 /* output format handling */
136 struct FFStream *stream;
137 /* -1 is invalid stream */
138 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
139 int switch_feed_streams[MAX_STREAMS]; /* index of streams in the feed */
140 int switch_pending;
141 AVFormatContext fmt_ctx; /* instance of FFStream for one user */
142 int last_packet_sent; /* true if last data packet was sent */
143 int suppress_log;
144 DataRateData datarate;
145 int wmp_client_id;
146 char protocol[16];
147 char method[16];
148 char url[128];
149 int buffer_size;
150 uint8_t *buffer;
151 int is_packetized; /* if true, the stream is packetized */
152 int packet_stream_index; /* current stream for output in state machine */
154 /* RTSP state specific */
155 uint8_t *pb_buffer; /* XXX: use that in all the code */
156 ByteIOContext *pb;
157 int seq; /* RTSP sequence number */
159 /* RTP state specific */
160 enum RTSPProtocol rtp_protocol;
161 char session_id[32]; /* session id */
162 AVFormatContext *rtp_ctx[MAX_STREAMS];
164 /* RTP/UDP specific */
165 URLContext *rtp_handles[MAX_STREAMS];
167 /* RTP/TCP specific */
168 struct HTTPContext *rtsp_c;
169 uint8_t *packet_buffer, *packet_buffer_ptr, *packet_buffer_end;
170 } HTTPContext;
172 /* each generated stream is described here */
173 enum StreamType {
174 STREAM_TYPE_LIVE,
175 STREAM_TYPE_STATUS,
176 STREAM_TYPE_REDIRECT,
179 enum IPAddressAction {
180 IP_ALLOW = 1,
181 IP_DENY,
184 typedef struct IPAddressACL {
185 struct IPAddressACL *next;
186 enum IPAddressAction action;
187 /* These are in host order */
188 struct in_addr first;
189 struct in_addr last;
190 } IPAddressACL;
192 /* description of each stream of the ffserver.conf file */
193 typedef struct FFStream {
194 enum StreamType stream_type;
195 char filename[1024]; /* stream filename */
196 struct FFStream *feed; /* feed we are using (can be null if
197 coming from file) */
198 AVFormatParameters *ap_in; /* input parameters */
199 AVInputFormat *ifmt; /* if non NULL, force input format */
200 AVOutputFormat *fmt;
201 IPAddressACL *acl;
202 int nb_streams;
203 int prebuffer; /* Number of millseconds early to start */
204 int64_t max_time; /* Number of milliseconds to run */
205 int send_on_key;
206 AVStream *streams[MAX_STREAMS];
207 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
208 char feed_filename[1024]; /* file name of the feed storage, or
209 input file name for a stream */
210 char author[512];
211 char title[512];
212 char copyright[512];
213 char comment[512];
214 pid_t pid; /* Of ffmpeg process */
215 time_t pid_start; /* Of ffmpeg process */
216 char **child_argv;
217 struct FFStream *next;
218 unsigned bandwidth; /* bandwidth, in kbits/s */
219 /* RTSP options */
220 char *rtsp_option;
221 /* multicast specific */
222 int is_multicast;
223 struct in_addr multicast_ip;
224 int multicast_port; /* first port used for multicast */
225 int multicast_ttl;
226 int loop; /* if true, send the stream in loops (only meaningful if file) */
228 /* feed specific */
229 int feed_opened; /* true if someone is writing to the feed */
230 int is_feed; /* true if it is a feed */
231 int readonly; /* True if writing is prohibited to the file */
232 int conns_served;
233 int64_t bytes_served;
234 int64_t feed_max_size; /* maximum storage size, zero means unlimited */
235 int64_t feed_write_index; /* current write position in feed (it wraps around) */
236 int64_t feed_size; /* current size of feed */
237 struct FFStream *next_feed;
238 } FFStream;
240 typedef struct FeedData {
241 long long data_count;
242 float avg_frame_size; /* frame size averaged over last frames with exponential mean */
243 } FeedData;
245 static struct sockaddr_in my_http_addr;
246 static struct sockaddr_in my_rtsp_addr;
248 static char logfilename[1024];
249 static HTTPContext *first_http_ctx;
250 static FFStream *first_feed; /* contains only feeds */
251 static FFStream *first_stream; /* contains all streams, including feeds */
253 static void new_connection(int server_fd, int is_rtsp);
254 static void close_connection(HTTPContext *c);
256 /* HTTP handling */
257 static int handle_connection(HTTPContext *c);
258 static int http_parse_request(HTTPContext *c);
259 static int http_send_data(HTTPContext *c);
260 static void compute_status(HTTPContext *c);
261 static int open_input_stream(HTTPContext *c, const char *info);
262 static int http_start_receive_data(HTTPContext *c);
263 static int http_receive_data(HTTPContext *c);
265 /* RTSP handling */
266 static int rtsp_parse_request(HTTPContext *c);
267 static void rtsp_cmd_describe(HTTPContext *c, const char *url);
268 static void rtsp_cmd_options(HTTPContext *c, const char *url);
269 static void rtsp_cmd_setup(HTTPContext *c, const char *url, RTSPHeader *h);
270 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h);
271 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h);
272 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h);
274 /* SDP handling */
275 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
276 struct in_addr my_ip);
278 /* RTP handling */
279 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
280 FFStream *stream, const char *session_id,
281 enum RTSPProtocol rtp_protocol);
282 static int rtp_new_av_stream(HTTPContext *c,
283 int stream_index, struct sockaddr_in *dest_addr,
284 HTTPContext *rtsp_c);
286 static const char *my_program_name;
287 static const char *my_program_dir;
289 static const char *config_filename;
290 static int ffserver_debug;
291 static int ffserver_daemon;
292 static int no_launch;
293 static int need_to_start_children;
295 static int nb_max_connections = 5;
296 static int nb_connections;
298 static uint64_t max_bandwidth = 1000;
299 static uint64_t current_bandwidth;
301 static int64_t cur_time; // Making this global saves on passing it around everywhere
303 static AVRandomState random_state;
305 static FILE *logfile = NULL;
307 static char *ctime1(char *buf2)
309 time_t ti;
310 char *p;
312 ti = time(NULL);
313 p = ctime(&ti);
314 strcpy(buf2, p);
315 p = buf2 + strlen(p) - 1;
316 if (*p == '\n')
317 *p = '\0';
318 return buf2;
321 static void http_vlog(const char *fmt, va_list vargs)
323 static int print_prefix = 1;
324 if (logfile) {
325 if (print_prefix) {
326 char buf[32];
327 ctime1(buf);
328 fprintf(logfile, "%s ", buf);
330 print_prefix = strstr(fmt, "\n") != NULL;
331 vfprintf(logfile, fmt, vargs);
332 fflush(logfile);
336 void __attribute__ ((format (printf, 1, 2))) http_log(const char *fmt, ...)
338 va_list vargs;
339 va_start(vargs, fmt);
340 http_vlog(fmt, vargs);
341 va_end(vargs);
344 static void http_av_log(void *ptr, int level, const char *fmt, va_list vargs)
346 static int print_prefix = 1;
347 AVClass *avc = ptr ? *(AVClass**)ptr : NULL;
348 if (level > av_log_level)
349 return;
350 if (print_prefix && avc)
351 http_log("[%s @ %p]", avc->item_name(ptr), avc);
352 print_prefix = strstr(fmt, "\n") != NULL;
353 http_vlog(fmt, vargs);
356 static void log_connection(HTTPContext *c)
358 if (c->suppress_log)
359 return;
361 http_log("%s - - [%s] \"%s %s\" %d %"PRId64"\n",
362 inet_ntoa(c->from_addr.sin_addr), c->method, c->url,
363 c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
366 static void update_datarate(DataRateData *drd, int64_t count)
368 if (!drd->time1 && !drd->count1) {
369 drd->time1 = drd->time2 = cur_time;
370 drd->count1 = drd->count2 = count;
371 } else if (cur_time - drd->time2 > 5000) {
372 drd->time1 = drd->time2;
373 drd->count1 = drd->count2;
374 drd->time2 = cur_time;
375 drd->count2 = count;
379 /* In bytes per second */
380 static int compute_datarate(DataRateData *drd, int64_t count)
382 if (cur_time == drd->time1)
383 return 0;
385 return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
389 static void start_children(FFStream *feed)
391 if (no_launch)
392 return;
394 for (; feed; feed = feed->next) {
395 if (feed->child_argv && !feed->pid) {
396 feed->pid_start = time(0);
398 feed->pid = fork();
400 if (feed->pid < 0) {
401 http_log("Unable to create children\n");
402 exit(1);
404 if (!feed->pid) {
405 /* In child */
406 char pathname[1024];
407 char *slash;
408 int i;
410 for (i = 3; i < 256; i++)
411 close(i);
413 if (!ffserver_debug) {
414 i = open("/dev/null", O_RDWR);
415 if (i != -1) {
416 dup2(i, 0);
417 dup2(i, 1);
418 dup2(i, 2);
419 close(i);
423 av_strlcpy(pathname, my_program_name, sizeof(pathname));
425 slash = strrchr(pathname, '/');
426 if (!slash)
427 slash = pathname;
428 else
429 slash++;
430 strcpy(slash, "ffmpeg");
432 /* This is needed to make relative pathnames work */
433 chdir(my_program_dir);
435 signal(SIGPIPE, SIG_DFL);
437 execvp(pathname, feed->child_argv);
439 _exit(1);
445 /* open a listening socket */
446 static int socket_open_listen(struct sockaddr_in *my_addr)
448 int server_fd, tmp;
450 server_fd = socket(AF_INET,SOCK_STREAM,0);
451 if (server_fd < 0) {
452 perror ("socket");
453 return -1;
456 tmp = 1;
457 setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
459 if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
460 char bindmsg[32];
461 snprintf(bindmsg, sizeof(bindmsg), "bind(port %d)", ntohs(my_addr->sin_port));
462 perror (bindmsg);
463 closesocket(server_fd);
464 return -1;
467 if (listen (server_fd, 5) < 0) {
468 perror ("listen");
469 closesocket(server_fd);
470 return -1;
472 ff_socket_nonblock(server_fd, 1);
474 return server_fd;
477 /* start all multicast streams */
478 static void start_multicast(void)
480 FFStream *stream;
481 char session_id[32];
482 HTTPContext *rtp_c;
483 struct sockaddr_in dest_addr;
484 int default_port, stream_index;
486 default_port = 6000;
487 for(stream = first_stream; stream != NULL; stream = stream->next) {
488 if (stream->is_multicast) {
489 /* open the RTP connection */
490 snprintf(session_id, sizeof(session_id), "%08x%08x",
491 av_random(&random_state), av_random(&random_state));
493 /* choose a port if none given */
494 if (stream->multicast_port == 0) {
495 stream->multicast_port = default_port;
496 default_port += 100;
499 dest_addr.sin_family = AF_INET;
500 dest_addr.sin_addr = stream->multicast_ip;
501 dest_addr.sin_port = htons(stream->multicast_port);
503 rtp_c = rtp_new_connection(&dest_addr, stream, session_id,
504 RTSP_PROTOCOL_RTP_UDP_MULTICAST);
505 if (!rtp_c)
506 continue;
508 if (open_input_stream(rtp_c, "") < 0) {
509 http_log("Could not open input stream for stream '%s'\n",
510 stream->filename);
511 continue;
514 /* open each RTP stream */
515 for(stream_index = 0; stream_index < stream->nb_streams;
516 stream_index++) {
517 dest_addr.sin_port = htons(stream->multicast_port +
518 2 * stream_index);
519 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, NULL) < 0) {
520 http_log("Could not open output stream '%s/streamid=%d'\n",
521 stream->filename, stream_index);
522 exit(1);
526 /* change state to send data */
527 rtp_c->state = HTTPSTATE_SEND_DATA;
532 /* main loop of the http server */
533 static int http_server(void)
535 int server_fd = 0, rtsp_server_fd = 0;
536 int ret, delay, delay1;
537 struct pollfd poll_table[HTTP_MAX_CONNECTIONS + 2], *poll_entry;
538 HTTPContext *c, *c_next;
540 if (my_http_addr.sin_port) {
541 server_fd = socket_open_listen(&my_http_addr);
542 if (server_fd < 0)
543 return -1;
546 if (my_rtsp_addr.sin_port) {
547 rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
548 if (rtsp_server_fd < 0)
549 return -1;
552 if (!rtsp_server_fd && !server_fd) {
553 http_log("HTTP and RTSP disabled.\n");
554 return -1;
557 http_log("ffserver started.\n");
559 start_children(first_feed);
561 first_http_ctx = NULL;
562 nb_connections = 0;
564 start_multicast();
566 for(;;) {
567 poll_entry = poll_table;
568 if (server_fd) {
569 poll_entry->fd = server_fd;
570 poll_entry->events = POLLIN;
571 poll_entry++;
573 if (rtsp_server_fd) {
574 poll_entry->fd = rtsp_server_fd;
575 poll_entry->events = POLLIN;
576 poll_entry++;
579 /* wait for events on each HTTP handle */
580 c = first_http_ctx;
581 delay = 1000;
582 while (c != NULL) {
583 int fd;
584 fd = c->fd;
585 switch(c->state) {
586 case HTTPSTATE_SEND_HEADER:
587 case RTSPSTATE_SEND_REPLY:
588 case RTSPSTATE_SEND_PACKET:
589 c->poll_entry = poll_entry;
590 poll_entry->fd = fd;
591 poll_entry->events = POLLOUT;
592 poll_entry++;
593 break;
594 case HTTPSTATE_SEND_DATA_HEADER:
595 case HTTPSTATE_SEND_DATA:
596 case HTTPSTATE_SEND_DATA_TRAILER:
597 if (!c->is_packetized) {
598 /* for TCP, we output as much as we can (may need to put a limit) */
599 c->poll_entry = poll_entry;
600 poll_entry->fd = fd;
601 poll_entry->events = POLLOUT;
602 poll_entry++;
603 } else {
604 /* when ffserver is doing the timing, we work by
605 looking at which packet need to be sent every
606 10 ms */
607 delay1 = 10; /* one tick wait XXX: 10 ms assumed */
608 if (delay1 < delay)
609 delay = delay1;
611 break;
612 case HTTPSTATE_WAIT_REQUEST:
613 case HTTPSTATE_RECEIVE_DATA:
614 case HTTPSTATE_WAIT_FEED:
615 case RTSPSTATE_WAIT_REQUEST:
616 /* need to catch errors */
617 c->poll_entry = poll_entry;
618 poll_entry->fd = fd;
619 poll_entry->events = POLLIN;/* Maybe this will work */
620 poll_entry++;
621 break;
622 default:
623 c->poll_entry = NULL;
624 break;
626 c = c->next;
629 /* wait for an event on one connection. We poll at least every
630 second to handle timeouts */
631 do {
632 ret = poll(poll_table, poll_entry - poll_table, delay);
633 if (ret < 0 && ff_neterrno() != FF_NETERROR(EAGAIN) &&
634 ff_neterrno() != FF_NETERROR(EINTR))
635 return -1;
636 } while (ret < 0);
638 cur_time = av_gettime() / 1000;
640 if (need_to_start_children) {
641 need_to_start_children = 0;
642 start_children(first_feed);
645 /* now handle the events */
646 for(c = first_http_ctx; c != NULL; c = c_next) {
647 c_next = c->next;
648 if (handle_connection(c) < 0) {
649 /* close and free the connection */
650 log_connection(c);
651 close_connection(c);
655 poll_entry = poll_table;
656 if (server_fd) {
657 /* new HTTP connection request ? */
658 if (poll_entry->revents & POLLIN)
659 new_connection(server_fd, 0);
660 poll_entry++;
662 if (rtsp_server_fd) {
663 /* new RTSP connection request ? */
664 if (poll_entry->revents & POLLIN)
665 new_connection(rtsp_server_fd, 1);
670 /* start waiting for a new HTTP/RTSP request */
671 static void start_wait_request(HTTPContext *c, int is_rtsp)
673 c->buffer_ptr = c->buffer;
674 c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */
676 if (is_rtsp) {
677 c->timeout = cur_time + RTSP_REQUEST_TIMEOUT;
678 c->state = RTSPSTATE_WAIT_REQUEST;
679 } else {
680 c->timeout = cur_time + HTTP_REQUEST_TIMEOUT;
681 c->state = HTTPSTATE_WAIT_REQUEST;
685 static void new_connection(int server_fd, int is_rtsp)
687 struct sockaddr_in from_addr;
688 int fd, len;
689 HTTPContext *c = NULL;
691 len = sizeof(from_addr);
692 fd = accept(server_fd, (struct sockaddr *)&from_addr,
693 &len);
694 if (fd < 0) {
695 http_log("error during accept %s\n", strerror(errno));
696 return;
698 ff_socket_nonblock(fd, 1);
700 /* XXX: should output a warning page when coming
701 close to the connection limit */
702 if (nb_connections >= nb_max_connections)
703 goto fail;
705 /* add a new connection */
706 c = av_mallocz(sizeof(HTTPContext));
707 if (!c)
708 goto fail;
710 c->fd = fd;
711 c->poll_entry = NULL;
712 c->from_addr = from_addr;
713 c->buffer_size = IOBUFFER_INIT_SIZE;
714 c->buffer = av_malloc(c->buffer_size);
715 if (!c->buffer)
716 goto fail;
718 c->next = first_http_ctx;
719 first_http_ctx = c;
720 nb_connections++;
722 start_wait_request(c, is_rtsp);
724 return;
726 fail:
727 if (c) {
728 av_free(c->buffer);
729 av_free(c);
731 closesocket(fd);
734 static void close_connection(HTTPContext *c)
736 HTTPContext **cp, *c1;
737 int i, nb_streams;
738 AVFormatContext *ctx;
739 URLContext *h;
740 AVStream *st;
742 /* remove connection from list */
743 cp = &first_http_ctx;
744 while ((*cp) != NULL) {
745 c1 = *cp;
746 if (c1 == c)
747 *cp = c->next;
748 else
749 cp = &c1->next;
752 /* remove references, if any (XXX: do it faster) */
753 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
754 if (c1->rtsp_c == c)
755 c1->rtsp_c = NULL;
758 /* remove connection associated resources */
759 if (c->fd >= 0)
760 closesocket(c->fd);
761 if (c->fmt_in) {
762 /* close each frame parser */
763 for(i=0;i<c->fmt_in->nb_streams;i++) {
764 st = c->fmt_in->streams[i];
765 if (st->codec->codec)
766 avcodec_close(st->codec);
768 av_close_input_file(c->fmt_in);
771 /* free RTP output streams if any */
772 nb_streams = 0;
773 if (c->stream)
774 nb_streams = c->stream->nb_streams;
776 for(i=0;i<nb_streams;i++) {
777 ctx = c->rtp_ctx[i];
778 if (ctx) {
779 av_write_trailer(ctx);
780 av_free(ctx);
782 h = c->rtp_handles[i];
783 if (h)
784 url_close(h);
787 ctx = &c->fmt_ctx;
789 if (!c->last_packet_sent) {
790 if (ctx->oformat) {
791 /* prepare header */
792 if (url_open_dyn_buf(&ctx->pb) >= 0) {
793 av_write_trailer(ctx);
794 av_freep(&c->pb_buffer);
795 url_close_dyn_buf(ctx->pb, &c->pb_buffer);
800 for(i=0; i<ctx->nb_streams; i++)
801 av_free(ctx->streams[i]);
803 if (c->stream && !c->post && c->stream->stream_type == STREAM_TYPE_LIVE)
804 current_bandwidth -= c->stream->bandwidth;
806 /* signal that there is no feed if we are the feeder socket */
807 if (c->state == HTTPSTATE_RECEIVE_DATA && c->stream) {
808 c->stream->feed_opened = 0;
809 close(c->feed_fd);
812 av_freep(&c->pb_buffer);
813 av_freep(&c->packet_buffer);
814 av_free(c->buffer);
815 av_free(c);
816 nb_connections--;
819 static int handle_connection(HTTPContext *c)
821 int len, ret;
823 switch(c->state) {
824 case HTTPSTATE_WAIT_REQUEST:
825 case RTSPSTATE_WAIT_REQUEST:
826 /* timeout ? */
827 if ((c->timeout - cur_time) < 0)
828 return -1;
829 if (c->poll_entry->revents & (POLLERR | POLLHUP))
830 return -1;
832 /* no need to read if no events */
833 if (!(c->poll_entry->revents & POLLIN))
834 return 0;
835 /* read the data */
836 read_loop:
837 len = recv(c->fd, c->buffer_ptr, 1, 0);
838 if (len < 0) {
839 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
840 ff_neterrno() != FF_NETERROR(EINTR))
841 return -1;
842 } else if (len == 0) {
843 return -1;
844 } else {
845 /* search for end of request. */
846 uint8_t *ptr;
847 c->buffer_ptr += len;
848 ptr = c->buffer_ptr;
849 if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
850 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
851 /* request found : parse it and reply */
852 if (c->state == HTTPSTATE_WAIT_REQUEST) {
853 ret = http_parse_request(c);
854 } else {
855 ret = rtsp_parse_request(c);
857 if (ret < 0)
858 return -1;
859 } else if (ptr >= c->buffer_end) {
860 /* request too long: cannot do anything */
861 return -1;
862 } else goto read_loop;
864 break;
866 case HTTPSTATE_SEND_HEADER:
867 if (c->poll_entry->revents & (POLLERR | POLLHUP))
868 return -1;
870 /* no need to write if no events */
871 if (!(c->poll_entry->revents & POLLOUT))
872 return 0;
873 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
874 if (len < 0) {
875 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
876 ff_neterrno() != FF_NETERROR(EINTR)) {
877 /* error : close connection */
878 av_freep(&c->pb_buffer);
879 return -1;
881 } else {
882 c->buffer_ptr += len;
883 if (c->stream)
884 c->stream->bytes_served += len;
885 c->data_count += len;
886 if (c->buffer_ptr >= c->buffer_end) {
887 av_freep(&c->pb_buffer);
888 /* if error, exit */
889 if (c->http_error)
890 return -1;
891 /* all the buffer was sent : synchronize to the incoming stream */
892 c->state = HTTPSTATE_SEND_DATA_HEADER;
893 c->buffer_ptr = c->buffer_end = c->buffer;
896 break;
898 case HTTPSTATE_SEND_DATA:
899 case HTTPSTATE_SEND_DATA_HEADER:
900 case HTTPSTATE_SEND_DATA_TRAILER:
901 /* for packetized output, we consider we can always write (the
902 input streams sets the speed). It may be better to verify
903 that we do not rely too much on the kernel queues */
904 if (!c->is_packetized) {
905 if (c->poll_entry->revents & (POLLERR | POLLHUP))
906 return -1;
908 /* no need to read if no events */
909 if (!(c->poll_entry->revents & POLLOUT))
910 return 0;
912 if (http_send_data(c) < 0)
913 return -1;
914 /* close connection if trailer sent */
915 if (c->state == HTTPSTATE_SEND_DATA_TRAILER)
916 return -1;
917 break;
918 case HTTPSTATE_RECEIVE_DATA:
919 /* no need to read if no events */
920 if (c->poll_entry->revents & (POLLERR | POLLHUP))
921 return -1;
922 if (!(c->poll_entry->revents & POLLIN))
923 return 0;
924 if (http_receive_data(c) < 0)
925 return -1;
926 break;
927 case HTTPSTATE_WAIT_FEED:
928 /* no need to read if no events */
929 if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
930 return -1;
932 /* nothing to do, we'll be waken up by incoming feed packets */
933 break;
935 case RTSPSTATE_SEND_REPLY:
936 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
937 av_freep(&c->pb_buffer);
938 return -1;
940 /* no need to write if no events */
941 if (!(c->poll_entry->revents & POLLOUT))
942 return 0;
943 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
944 if (len < 0) {
945 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
946 ff_neterrno() != FF_NETERROR(EINTR)) {
947 /* error : close connection */
948 av_freep(&c->pb_buffer);
949 return -1;
951 } else {
952 c->buffer_ptr += len;
953 c->data_count += len;
954 if (c->buffer_ptr >= c->buffer_end) {
955 /* all the buffer was sent : wait for a new request */
956 av_freep(&c->pb_buffer);
957 start_wait_request(c, 1);
960 break;
961 case RTSPSTATE_SEND_PACKET:
962 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
963 av_freep(&c->packet_buffer);
964 return -1;
966 /* no need to write if no events */
967 if (!(c->poll_entry->revents & POLLOUT))
968 return 0;
969 len = send(c->fd, c->packet_buffer_ptr,
970 c->packet_buffer_end - c->packet_buffer_ptr, 0);
971 if (len < 0) {
972 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
973 ff_neterrno() != FF_NETERROR(EINTR)) {
974 /* error : close connection */
975 av_freep(&c->packet_buffer);
976 return -1;
978 } else {
979 c->packet_buffer_ptr += len;
980 if (c->packet_buffer_ptr >= c->packet_buffer_end) {
981 /* all the buffer was sent : wait for a new request */
982 av_freep(&c->packet_buffer);
983 c->state = RTSPSTATE_WAIT_REQUEST;
986 break;
987 case HTTPSTATE_READY:
988 /* nothing to do */
989 break;
990 default:
991 return -1;
993 return 0;
996 static int extract_rates(char *rates, int ratelen, const char *request)
998 const char *p;
1000 for (p = request; *p && *p != '\r' && *p != '\n'; ) {
1001 if (strncasecmp(p, "Pragma:", 7) == 0) {
1002 const char *q = p + 7;
1004 while (*q && *q != '\n' && isspace(*q))
1005 q++;
1007 if (strncasecmp(q, "stream-switch-entry=", 20) == 0) {
1008 int stream_no;
1009 int rate_no;
1011 q += 20;
1013 memset(rates, 0xff, ratelen);
1015 while (1) {
1016 while (*q && *q != '\n' && *q != ':')
1017 q++;
1019 if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2)
1020 break;
1022 stream_no--;
1023 if (stream_no < ratelen && stream_no >= 0)
1024 rates[stream_no] = rate_no;
1026 while (*q && *q != '\n' && !isspace(*q))
1027 q++;
1030 return 1;
1033 p = strchr(p, '\n');
1034 if (!p)
1035 break;
1037 p++;
1040 return 0;
1043 static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
1045 int i;
1046 int best_bitrate = 100000000;
1047 int best = -1;
1049 for (i = 0; i < feed->nb_streams; i++) {
1050 AVCodecContext *feed_codec = feed->streams[i]->codec;
1052 if (feed_codec->codec_id != codec->codec_id ||
1053 feed_codec->sample_rate != codec->sample_rate ||
1054 feed_codec->width != codec->width ||
1055 feed_codec->height != codec->height)
1056 continue;
1058 /* Potential stream */
1060 /* We want the fastest stream less than bit_rate, or the slowest
1061 * faster than bit_rate
1064 if (feed_codec->bit_rate <= bit_rate) {
1065 if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) {
1066 best_bitrate = feed_codec->bit_rate;
1067 best = i;
1069 } else {
1070 if (feed_codec->bit_rate < best_bitrate) {
1071 best_bitrate = feed_codec->bit_rate;
1072 best = i;
1077 return best;
1080 static int modify_current_stream(HTTPContext *c, char *rates)
1082 int i;
1083 FFStream *req = c->stream;
1084 int action_required = 0;
1086 /* Not much we can do for a feed */
1087 if (!req->feed)
1088 return 0;
1090 for (i = 0; i < req->nb_streams; i++) {
1091 AVCodecContext *codec = req->streams[i]->codec;
1093 switch(rates[i]) {
1094 case 0:
1095 c->switch_feed_streams[i] = req->feed_streams[i];
1096 break;
1097 case 1:
1098 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
1099 break;
1100 case 2:
1101 /* Wants off or slow */
1102 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
1103 #ifdef WANTS_OFF
1104 /* This doesn't work well when it turns off the only stream! */
1105 c->switch_feed_streams[i] = -2;
1106 c->feed_streams[i] = -2;
1107 #endif
1108 break;
1111 if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
1112 action_required = 1;
1115 return action_required;
1119 static void do_switch_stream(HTTPContext *c, int i)
1121 if (c->switch_feed_streams[i] >= 0) {
1122 #ifdef PHILIP
1123 c->feed_streams[i] = c->switch_feed_streams[i];
1124 #endif
1126 /* Now update the stream */
1128 c->switch_feed_streams[i] = -1;
1131 /* XXX: factorize in utils.c ? */
1132 /* XXX: take care with different space meaning */
1133 static void skip_spaces(const char **pp)
1135 const char *p;
1136 p = *pp;
1137 while (*p == ' ' || *p == '\t')
1138 p++;
1139 *pp = p;
1142 static void get_word(char *buf, int buf_size, const char **pp)
1144 const char *p;
1145 char *q;
1147 p = *pp;
1148 skip_spaces(&p);
1149 q = buf;
1150 while (!isspace(*p) && *p != '\0') {
1151 if ((q - buf) < buf_size - 1)
1152 *q++ = *p;
1153 p++;
1155 if (buf_size > 0)
1156 *q = '\0';
1157 *pp = p;
1160 static int validate_acl(FFStream *stream, HTTPContext *c)
1162 enum IPAddressAction last_action = IP_DENY;
1163 IPAddressACL *acl;
1164 struct in_addr *src = &c->from_addr.sin_addr;
1165 unsigned long src_addr = src->s_addr;
1167 for (acl = stream->acl; acl; acl = acl->next) {
1168 if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr)
1169 return (acl->action == IP_ALLOW) ? 1 : 0;
1170 last_action = acl->action;
1173 /* Nothing matched, so return not the last action */
1174 return (last_action == IP_DENY) ? 1 : 0;
1177 /* compute the real filename of a file by matching it without its
1178 extensions to all the stream filenames */
1179 static void compute_real_filename(char *filename, int max_size)
1181 char file1[1024];
1182 char file2[1024];
1183 char *p;
1184 FFStream *stream;
1186 /* compute filename by matching without the file extensions */
1187 av_strlcpy(file1, filename, sizeof(file1));
1188 p = strrchr(file1, '.');
1189 if (p)
1190 *p = '\0';
1191 for(stream = first_stream; stream != NULL; stream = stream->next) {
1192 av_strlcpy(file2, stream->filename, sizeof(file2));
1193 p = strrchr(file2, '.');
1194 if (p)
1195 *p = '\0';
1196 if (!strcmp(file1, file2)) {
1197 av_strlcpy(filename, stream->filename, max_size);
1198 break;
1203 enum RedirType {
1204 REDIR_NONE,
1205 REDIR_ASX,
1206 REDIR_RAM,
1207 REDIR_ASF,
1208 REDIR_RTSP,
1209 REDIR_SDP,
1212 /* parse http request and prepare header */
1213 static int http_parse_request(HTTPContext *c)
1215 char *p;
1216 enum RedirType redir_type;
1217 char cmd[32];
1218 char info[1024], filename[1024];
1219 char url[1024], *q;
1220 char protocol[32];
1221 char msg[1024];
1222 const char *mime_type;
1223 FFStream *stream;
1224 int i;
1225 char ratebuf[32];
1226 char *useragent = 0;
1228 p = c->buffer;
1229 get_word(cmd, sizeof(cmd), (const char **)&p);
1230 av_strlcpy(c->method, cmd, sizeof(c->method));
1232 if (!strcmp(cmd, "GET"))
1233 c->post = 0;
1234 else if (!strcmp(cmd, "POST"))
1235 c->post = 1;
1236 else
1237 return -1;
1239 get_word(url, sizeof(url), (const char **)&p);
1240 av_strlcpy(c->url, url, sizeof(c->url));
1242 get_word(protocol, sizeof(protocol), (const char **)&p);
1243 if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
1244 return -1;
1246 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
1248 if (ffserver_debug)
1249 http_log("New connection: %s %s\n", cmd, url);
1251 /* find the filename and the optional info string in the request */
1252 p = strchr(url, '?');
1253 if (p) {
1254 av_strlcpy(info, p, sizeof(info));
1255 *p = '\0';
1256 } else
1257 info[0] = '\0';
1259 av_strlcpy(filename, url + ((*url == '/') ? 1 : 0), sizeof(filename)-1);
1261 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1262 if (strncasecmp(p, "User-Agent:", 11) == 0) {
1263 useragent = p + 11;
1264 if (*useragent && *useragent != '\n' && isspace(*useragent))
1265 useragent++;
1266 break;
1268 p = strchr(p, '\n');
1269 if (!p)
1270 break;
1272 p++;
1275 redir_type = REDIR_NONE;
1276 if (match_ext(filename, "asx")) {
1277 redir_type = REDIR_ASX;
1278 filename[strlen(filename)-1] = 'f';
1279 } else if (match_ext(filename, "asf") &&
1280 (!useragent || strncasecmp(useragent, "NSPlayer", 8) != 0)) {
1281 /* if this isn't WMP or lookalike, return the redirector file */
1282 redir_type = REDIR_ASF;
1283 } else if (match_ext(filename, "rpm,ram")) {
1284 redir_type = REDIR_RAM;
1285 strcpy(filename + strlen(filename)-2, "m");
1286 } else if (match_ext(filename, "rtsp")) {
1287 redir_type = REDIR_RTSP;
1288 compute_real_filename(filename, sizeof(filename) - 1);
1289 } else if (match_ext(filename, "sdp")) {
1290 redir_type = REDIR_SDP;
1291 compute_real_filename(filename, sizeof(filename) - 1);
1294 // "redirect" / request to index.html
1295 if (!strlen(filename))
1296 av_strlcpy(filename, "index.html", sizeof(filename) - 1);
1298 stream = first_stream;
1299 while (stream != NULL) {
1300 if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
1301 break;
1302 stream = stream->next;
1304 if (stream == NULL) {
1305 snprintf(msg, sizeof(msg), "File '%s' not found", url);
1306 goto send_error;
1309 c->stream = stream;
1310 memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
1311 memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
1313 if (stream->stream_type == STREAM_TYPE_REDIRECT) {
1314 c->http_error = 301;
1315 q = c->buffer;
1316 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 301 Moved\r\n");
1317 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Location: %s\r\n", stream->feed_filename);
1318 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: text/html\r\n");
1319 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1320 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<html><head><title>Moved</title></head><body>\r\n");
1321 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "You should be <a href=\"%s\">redirected</a>.\r\n", stream->feed_filename);
1322 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</body></html>\r\n");
1324 /* prepare output buffer */
1325 c->buffer_ptr = c->buffer;
1326 c->buffer_end = q;
1327 c->state = HTTPSTATE_SEND_HEADER;
1328 return 0;
1331 /* If this is WMP, get the rate information */
1332 if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1333 if (modify_current_stream(c, ratebuf)) {
1334 for (i = 0; i < sizeof(c->feed_streams) / sizeof(c->feed_streams[0]); i++) {
1335 if (c->switch_feed_streams[i] >= 0)
1336 do_switch_stream(c, i);
1341 /* If already streaming this feed, do not let start another feeder. */
1342 if (stream->feed_opened) {
1343 snprintf(msg, sizeof(msg), "This feed is already being received.");
1344 goto send_error;
1347 if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE)
1348 current_bandwidth += stream->bandwidth;
1350 if (c->post == 0 && max_bandwidth < current_bandwidth) {
1351 c->http_error = 200;
1352 q = c->buffer;
1353 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 Server too busy\r\n");
1354 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: text/html\r\n");
1355 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1356 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<html><head><title>Too busy</title></head><body>\r\n");
1357 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<p>The server is too busy to serve your request at this time.</p>\r\n");
1358 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<p>The bandwidth being served (including your stream) is %lldkbit/sec, and this exceeds the limit of %lldkbit/sec.</p>\r\n",
1359 current_bandwidth, max_bandwidth);
1360 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</body></html>\r\n");
1362 /* prepare output buffer */
1363 c->buffer_ptr = c->buffer;
1364 c->buffer_end = q;
1365 c->state = HTTPSTATE_SEND_HEADER;
1366 return 0;
1369 if (redir_type != REDIR_NONE) {
1370 char *hostinfo = 0;
1372 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1373 if (strncasecmp(p, "Host:", 5) == 0) {
1374 hostinfo = p + 5;
1375 break;
1377 p = strchr(p, '\n');
1378 if (!p)
1379 break;
1381 p++;
1384 if (hostinfo) {
1385 char *eoh;
1386 char hostbuf[260];
1388 while (isspace(*hostinfo))
1389 hostinfo++;
1391 eoh = strchr(hostinfo, '\n');
1392 if (eoh) {
1393 if (eoh[-1] == '\r')
1394 eoh--;
1396 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
1397 memcpy(hostbuf, hostinfo, eoh - hostinfo);
1398 hostbuf[eoh - hostinfo] = 0;
1400 c->http_error = 200;
1401 q = c->buffer;
1402 switch(redir_type) {
1403 case REDIR_ASX:
1404 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 ASX Follows\r\n");
1405 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: video/x-ms-asf\r\n");
1406 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1407 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<ASX Version=\"3\">\r\n");
1408 //q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<!-- Autogenerated by ffserver -->\r\n");
1409 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n",
1410 hostbuf, filename, info);
1411 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</ASX>\r\n");
1412 break;
1413 case REDIR_RAM:
1414 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 RAM Follows\r\n");
1415 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: audio/x-pn-realaudio\r\n");
1416 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1417 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "# Autogenerated by ffserver\r\n");
1418 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "http://%s/%s%s\r\n",
1419 hostbuf, filename, info);
1420 break;
1421 case REDIR_ASF:
1422 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 ASF Redirect follows\r\n");
1423 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: video/x-ms-asf\r\n");
1424 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1425 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "[Reference]\r\n");
1426 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Ref1=http://%s/%s%s\r\n",
1427 hostbuf, filename, info);
1428 break;
1429 case REDIR_RTSP:
1431 char hostname[256], *p;
1432 /* extract only hostname */
1433 av_strlcpy(hostname, hostbuf, sizeof(hostname));
1434 p = strrchr(hostname, ':');
1435 if (p)
1436 *p = '\0';
1437 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 RTSP Redirect follows\r\n");
1438 /* XXX: incorrect mime type ? */
1439 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: application/x-rtsp\r\n");
1440 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1441 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "rtsp://%s:%d/%s\r\n",
1442 hostname, ntohs(my_rtsp_addr.sin_port),
1443 filename);
1445 break;
1446 case REDIR_SDP:
1448 uint8_t *sdp_data;
1449 int sdp_data_size, len;
1450 struct sockaddr_in my_addr;
1452 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1453 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: application/sdp\r\n");
1454 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1456 len = sizeof(my_addr);
1457 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
1459 /* XXX: should use a dynamic buffer */
1460 sdp_data_size = prepare_sdp_description(stream,
1461 &sdp_data,
1462 my_addr.sin_addr);
1463 if (sdp_data_size > 0) {
1464 memcpy(q, sdp_data, sdp_data_size);
1465 q += sdp_data_size;
1466 *q = '\0';
1467 av_free(sdp_data);
1470 break;
1471 default:
1472 abort();
1473 break;
1476 /* prepare output buffer */
1477 c->buffer_ptr = c->buffer;
1478 c->buffer_end = q;
1479 c->state = HTTPSTATE_SEND_HEADER;
1480 return 0;
1485 snprintf(msg, sizeof(msg), "ASX/RAM file not handled");
1486 goto send_error;
1489 stream->conns_served++;
1491 /* XXX: add there authenticate and IP match */
1493 if (c->post) {
1494 /* if post, it means a feed is being sent */
1495 if (!stream->is_feed) {
1496 /* However it might be a status report from WMP! Lets log the data
1497 * as it might come in handy one day
1499 char *logline = 0;
1500 int client_id = 0;
1502 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1503 if (strncasecmp(p, "Pragma: log-line=", 17) == 0) {
1504 logline = p;
1505 break;
1507 if (strncasecmp(p, "Pragma: client-id=", 18) == 0)
1508 client_id = strtol(p + 18, 0, 10);
1509 p = strchr(p, '\n');
1510 if (!p)
1511 break;
1513 p++;
1516 if (logline) {
1517 char *eol = strchr(logline, '\n');
1519 logline += 17;
1521 if (eol) {
1522 if (eol[-1] == '\r')
1523 eol--;
1524 http_log("%.*s\n", (int) (eol - logline), logline);
1525 c->suppress_log = 1;
1529 #ifdef DEBUG_WMP
1530 http_log("\nGot request:\n%s\n", c->buffer);
1531 #endif
1533 if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1534 HTTPContext *wmpc;
1536 /* Now we have to find the client_id */
1537 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
1538 if (wmpc->wmp_client_id == client_id)
1539 break;
1542 if (wmpc && modify_current_stream(wmpc, ratebuf))
1543 wmpc->switch_pending = 1;
1546 snprintf(msg, sizeof(msg), "POST command not handled");
1547 c->stream = 0;
1548 goto send_error;
1550 if (http_start_receive_data(c) < 0) {
1551 snprintf(msg, sizeof(msg), "could not open feed");
1552 goto send_error;
1554 c->http_error = 0;
1555 c->state = HTTPSTATE_RECEIVE_DATA;
1556 return 0;
1559 #ifdef DEBUG_WMP
1560 if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0)
1561 http_log("\nGot request:\n%s\n", c->buffer);
1562 #endif
1564 if (c->stream->stream_type == STREAM_TYPE_STATUS)
1565 goto send_status;
1567 /* open input stream */
1568 if (open_input_stream(c, info) < 0) {
1569 snprintf(msg, sizeof(msg), "Input stream corresponding to '%s' not found", url);
1570 goto send_error;
1573 /* prepare http header */
1574 q = c->buffer;
1575 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1576 mime_type = c->stream->fmt->mime_type;
1577 if (!mime_type)
1578 mime_type = "application/x-octet-stream";
1579 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Pragma: no-cache\r\n");
1581 /* for asf, we need extra headers */
1582 if (!strcmp(c->stream->fmt->name,"asf_stream")) {
1583 /* Need to allocate a client id */
1585 c->wmp_client_id = av_random(&random_state) & 0x7fffffff;
1587 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);
1589 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-Type: %s\r\n", mime_type);
1590 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1592 /* prepare output buffer */
1593 c->http_error = 0;
1594 c->buffer_ptr = c->buffer;
1595 c->buffer_end = q;
1596 c->state = HTTPSTATE_SEND_HEADER;
1597 return 0;
1598 send_error:
1599 c->http_error = 404;
1600 q = c->buffer;
1601 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 404 Not Found\r\n");
1602 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: %s\r\n", "text/html");
1603 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1604 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<HTML>\n");
1605 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n");
1606 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<BODY>%s</BODY>\n", msg);
1607 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</HTML>\n");
1609 /* prepare output buffer */
1610 c->buffer_ptr = c->buffer;
1611 c->buffer_end = q;
1612 c->state = HTTPSTATE_SEND_HEADER;
1613 return 0;
1614 send_status:
1615 compute_status(c);
1616 c->http_error = 200; /* horrible : we use this value to avoid
1617 going to the send data state */
1618 c->state = HTTPSTATE_SEND_HEADER;
1619 return 0;
1622 static void fmt_bytecount(ByteIOContext *pb, int64_t count)
1624 static const char *suffix = " kMGTP";
1625 const char *s;
1627 for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++);
1629 url_fprintf(pb, "%"PRId64"%c", count, *s);
1632 static void compute_status(HTTPContext *c)
1634 HTTPContext *c1;
1635 FFStream *stream;
1636 char *p;
1637 time_t ti;
1638 int i, len;
1639 ByteIOContext *pb;
1641 if (url_open_dyn_buf(&pb) < 0) {
1642 /* XXX: return an error ? */
1643 c->buffer_ptr = c->buffer;
1644 c->buffer_end = c->buffer;
1645 return;
1648 url_fprintf(pb, "HTTP/1.0 200 OK\r\n");
1649 url_fprintf(pb, "Content-type: %s\r\n", "text/html");
1650 url_fprintf(pb, "Pragma: no-cache\r\n");
1651 url_fprintf(pb, "\r\n");
1653 url_fprintf(pb, "<HEAD><TITLE>%s Status</TITLE>\n", program_name);
1654 if (c->stream->feed_filename[0])
1655 url_fprintf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
1656 url_fprintf(pb, "</HEAD>\n<BODY>");
1657 url_fprintf(pb, "<H1>%s Status</H1>\n", program_name);
1658 /* format status */
1659 url_fprintf(pb, "<H2>Available Streams</H2>\n");
1660 url_fprintf(pb, "<TABLE cellspacing=0 cellpadding=4>\n");
1661 url_fprintf(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");
1662 stream = first_stream;
1663 while (stream != NULL) {
1664 char sfilename[1024];
1665 char *eosf;
1667 if (stream->feed != stream) {
1668 av_strlcpy(sfilename, stream->filename, sizeof(sfilename) - 10);
1669 eosf = sfilename + strlen(sfilename);
1670 if (eosf - sfilename >= 4) {
1671 if (strcmp(eosf - 4, ".asf") == 0)
1672 strcpy(eosf - 4, ".asx");
1673 else if (strcmp(eosf - 3, ".rm") == 0)
1674 strcpy(eosf - 3, ".ram");
1675 else if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
1676 /* generate a sample RTSP director if
1677 unicast. Generate an SDP redirector if
1678 multicast */
1679 eosf = strrchr(sfilename, '.');
1680 if (!eosf)
1681 eosf = sfilename + strlen(sfilename);
1682 if (stream->is_multicast)
1683 strcpy(eosf, ".sdp");
1684 else
1685 strcpy(eosf, ".rtsp");
1689 url_fprintf(pb, "<TR><TD><A HREF=\"/%s\">%s</A> ",
1690 sfilename, stream->filename);
1691 url_fprintf(pb, "<td align=right> %d <td align=right> ",
1692 stream->conns_served);
1693 fmt_bytecount(pb, stream->bytes_served);
1694 switch(stream->stream_type) {
1695 case STREAM_TYPE_LIVE: {
1696 int audio_bit_rate = 0;
1697 int video_bit_rate = 0;
1698 const char *audio_codec_name = "";
1699 const char *video_codec_name = "";
1700 const char *audio_codec_name_extra = "";
1701 const char *video_codec_name_extra = "";
1703 for(i=0;i<stream->nb_streams;i++) {
1704 AVStream *st = stream->streams[i];
1705 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1706 switch(st->codec->codec_type) {
1707 case CODEC_TYPE_AUDIO:
1708 audio_bit_rate += st->codec->bit_rate;
1709 if (codec) {
1710 if (*audio_codec_name)
1711 audio_codec_name_extra = "...";
1712 audio_codec_name = codec->name;
1714 break;
1715 case CODEC_TYPE_VIDEO:
1716 video_bit_rate += st->codec->bit_rate;
1717 if (codec) {
1718 if (*video_codec_name)
1719 video_codec_name_extra = "...";
1720 video_codec_name = codec->name;
1722 break;
1723 case CODEC_TYPE_DATA:
1724 video_bit_rate += st->codec->bit_rate;
1725 break;
1726 default:
1727 abort();
1730 url_fprintf(pb, "<TD align=center> %s <TD align=right> %d <TD align=right> %d <TD> %s %s <TD align=right> %d <TD> %s %s",
1731 stream->fmt->name,
1732 stream->bandwidth,
1733 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
1734 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
1735 if (stream->feed)
1736 url_fprintf(pb, "<TD>%s", stream->feed->filename);
1737 else
1738 url_fprintf(pb, "<TD>%s", stream->feed_filename);
1739 url_fprintf(pb, "\n");
1741 break;
1742 default:
1743 url_fprintf(pb, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
1744 break;
1747 stream = stream->next;
1749 url_fprintf(pb, "</TABLE>\n");
1751 stream = first_stream;
1752 while (stream != NULL) {
1753 if (stream->feed == stream) {
1754 url_fprintf(pb, "<h2>Feed %s</h2>", stream->filename);
1755 if (stream->pid) {
1756 url_fprintf(pb, "Running as pid %d.\n", stream->pid);
1758 #if defined(linux) && !defined(CONFIG_NOCUTILS)
1760 FILE *pid_stat;
1761 char ps_cmd[64];
1763 /* This is somewhat linux specific I guess */
1764 snprintf(ps_cmd, sizeof(ps_cmd),
1765 "ps -o \"%%cpu,cputime\" --no-headers %d",
1766 stream->pid);
1768 pid_stat = popen(ps_cmd, "r");
1769 if (pid_stat) {
1770 char cpuperc[10];
1771 char cpuused[64];
1773 if (fscanf(pid_stat, "%10s %64s", cpuperc,
1774 cpuused) == 2) {
1775 url_fprintf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
1776 cpuperc, cpuused);
1778 fclose(pid_stat);
1781 #endif
1783 url_fprintf(pb, "<p>");
1785 url_fprintf(pb, "<table cellspacing=0 cellpadding=4><tr><th>Stream<th>type<th>kbits/s<th align=left>codec<th align=left>Parameters\n");
1787 for (i = 0; i < stream->nb_streams; i++) {
1788 AVStream *st = stream->streams[i];
1789 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1790 const char *type = "unknown";
1791 char parameters[64];
1793 parameters[0] = 0;
1795 switch(st->codec->codec_type) {
1796 case CODEC_TYPE_AUDIO:
1797 type = "audio";
1798 snprintf(parameters, sizeof(parameters), "%d channel(s), %d Hz", st->codec->channels, st->codec->sample_rate);
1799 break;
1800 case CODEC_TYPE_VIDEO:
1801 type = "video";
1802 snprintf(parameters, sizeof(parameters), "%dx%d, q=%d-%d, fps=%d", st->codec->width, st->codec->height,
1803 st->codec->qmin, st->codec->qmax, st->codec->time_base.den / st->codec->time_base.num);
1804 break;
1805 default:
1806 abort();
1808 url_fprintf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
1809 i, type, st->codec->bit_rate/1000, codec ? codec->name : "", parameters);
1811 url_fprintf(pb, "</table>\n");
1814 stream = stream->next;
1817 #if 0
1819 float avg;
1820 AVCodecContext *enc;
1821 char buf[1024];
1823 /* feed status */
1824 stream = first_feed;
1825 while (stream != NULL) {
1826 url_fprintf(pb, "<H1>Feed '%s'</H1>\n", stream->filename);
1827 url_fprintf(pb, "<TABLE>\n");
1828 url_fprintf(pb, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n");
1829 for(i=0;i<stream->nb_streams;i++) {
1830 AVStream *st = stream->streams[i];
1831 FeedData *fdata = st->priv_data;
1832 enc = st->codec;
1834 avcodec_string(buf, sizeof(buf), enc);
1835 avg = fdata->avg_frame_size * (float)enc->rate * 8.0;
1836 if (enc->codec->type == CODEC_TYPE_AUDIO && enc->frame_size > 0)
1837 avg /= enc->frame_size;
1838 url_fprintf(pb, "<TR><TD>%s <TD> %d <TD> %"PRId64" <TD> %0.1f\n",
1839 buf, enc->frame_number, fdata->data_count, avg / 1000.0);
1841 url_fprintf(pb, "</TABLE>\n");
1842 stream = stream->next_feed;
1845 #endif
1847 /* connection status */
1848 url_fprintf(pb, "<H2>Connection Status</H2>\n");
1850 url_fprintf(pb, "Number of connections: %d / %d<BR>\n",
1851 nb_connections, nb_max_connections);
1853 url_fprintf(pb, "Bandwidth in use: %lldk / %lldk<BR>\n",
1854 current_bandwidth, max_bandwidth);
1856 url_fprintf(pb, "<TABLE>\n");
1857 url_fprintf(pb, "<TR><th>#<th>File<th>IP<th>Proto<th>State<th>Target bits/sec<th>Actual bits/sec<th>Bytes transferred\n");
1858 c1 = first_http_ctx;
1859 i = 0;
1860 while (c1 != NULL) {
1861 int bitrate;
1862 int j;
1864 bitrate = 0;
1865 if (c1->stream) {
1866 for (j = 0; j < c1->stream->nb_streams; j++) {
1867 if (!c1->stream->feed)
1868 bitrate += c1->stream->streams[j]->codec->bit_rate;
1869 else if (c1->feed_streams[j] >= 0)
1870 bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec->bit_rate;
1874 i++;
1875 p = inet_ntoa(c1->from_addr.sin_addr);
1876 url_fprintf(pb, "<TR><TD><B>%d</B><TD>%s%s<TD>%s<TD>%s<TD>%s<td align=right>",
1878 c1->stream ? c1->stream->filename : "",
1879 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
1881 c1->protocol,
1882 http_state[c1->state]);
1883 fmt_bytecount(pb, bitrate);
1884 url_fprintf(pb, "<td align=right>");
1885 fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
1886 url_fprintf(pb, "<td align=right>");
1887 fmt_bytecount(pb, c1->data_count);
1888 url_fprintf(pb, "\n");
1889 c1 = c1->next;
1891 url_fprintf(pb, "</TABLE>\n");
1893 /* date */
1894 ti = time(NULL);
1895 p = ctime(&ti);
1896 url_fprintf(pb, "<HR size=1 noshade>Generated at %s", p);
1897 url_fprintf(pb, "</BODY>\n</HTML>\n");
1899 len = url_close_dyn_buf(pb, &c->pb_buffer);
1900 c->buffer_ptr = c->pb_buffer;
1901 c->buffer_end = c->pb_buffer + len;
1904 /* check if the parser needs to be opened for stream i */
1905 static void open_parser(AVFormatContext *s, int i)
1907 AVStream *st = s->streams[i];
1908 AVCodec *codec;
1910 if (!st->codec->codec) {
1911 codec = avcodec_find_decoder(st->codec->codec_id);
1912 if (codec && (codec->capabilities & CODEC_CAP_PARSE_ONLY)) {
1913 st->codec->parse_only = 1;
1914 if (avcodec_open(st->codec, codec) < 0)
1915 st->codec->parse_only = 0;
1920 static int open_input_stream(HTTPContext *c, const char *info)
1922 char buf[128];
1923 char input_filename[1024];
1924 AVFormatContext *s;
1925 int buf_size, i, ret;
1926 int64_t stream_pos;
1928 /* find file name */
1929 if (c->stream->feed) {
1930 strcpy(input_filename, c->stream->feed->feed_filename);
1931 buf_size = FFM_PACKET_SIZE;
1932 /* compute position (absolute time) */
1933 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1934 stream_pos = parse_date(buf, 0);
1935 if (stream_pos == INT64_MIN)
1936 return -1;
1937 } else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
1938 int prebuffer = strtol(buf, 0, 10);
1939 stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
1940 } else
1941 stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
1942 } else {
1943 strcpy(input_filename, c->stream->feed_filename);
1944 buf_size = 0;
1945 /* compute position (relative time) */
1946 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1947 stream_pos = parse_date(buf, 1);
1948 if (stream_pos == INT64_MIN)
1949 return -1;
1950 } else
1951 stream_pos = 0;
1953 if (input_filename[0] == '\0')
1954 return -1;
1956 #if 0
1957 { time_t when = stream_pos / 1000000;
1958 http_log("Stream pos = %"PRId64", time=%s", stream_pos, ctime(&when));
1960 #endif
1962 /* open stream */
1963 if ((ret = av_open_input_file(&s, input_filename, c->stream->ifmt,
1964 buf_size, c->stream->ap_in)) < 0) {
1965 http_log("could not open %s: %d\n", input_filename, ret);
1966 return -1;
1968 s->flags |= AVFMT_FLAG_GENPTS;
1969 c->fmt_in = s;
1970 av_find_stream_info(c->fmt_in);
1972 /* open each parser */
1973 for(i=0;i<s->nb_streams;i++)
1974 open_parser(s, i);
1976 /* choose stream as clock source (we favorize video stream if
1977 present) for packet sending */
1978 c->pts_stream_index = 0;
1979 for(i=0;i<c->stream->nb_streams;i++) {
1980 if (c->pts_stream_index == 0 &&
1981 c->stream->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO) {
1982 c->pts_stream_index = i;
1986 #if 1
1987 if (c->fmt_in->iformat->read_seek)
1988 av_seek_frame(c->fmt_in, -1, stream_pos, 0);
1989 #endif
1990 /* set the start time (needed for maxtime and RTP packet timing) */
1991 c->start_time = cur_time;
1992 c->first_pts = AV_NOPTS_VALUE;
1993 return 0;
1996 /* return the server clock (in us) */
1997 static int64_t get_server_clock(HTTPContext *c)
1999 /* compute current pts value from system time */
2000 return (cur_time - c->start_time) * 1000;
2003 /* return the estimated time at which the current packet must be sent
2004 (in us) */
2005 static int64_t get_packet_send_clock(HTTPContext *c)
2007 int bytes_left, bytes_sent, frame_bytes;
2009 frame_bytes = c->cur_frame_bytes;
2010 if (frame_bytes <= 0)
2011 return c->cur_pts;
2012 else {
2013 bytes_left = c->buffer_end - c->buffer_ptr;
2014 bytes_sent = frame_bytes - bytes_left;
2015 return c->cur_pts + (c->cur_frame_duration * bytes_sent) / frame_bytes;
2020 static int http_prepare_data(HTTPContext *c)
2022 int i, len, ret;
2023 AVFormatContext *ctx;
2025 av_freep(&c->pb_buffer);
2026 switch(c->state) {
2027 case HTTPSTATE_SEND_DATA_HEADER:
2028 memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
2029 av_strlcpy(c->fmt_ctx.author, c->stream->author,
2030 sizeof(c->fmt_ctx.author));
2031 av_strlcpy(c->fmt_ctx.comment, c->stream->comment,
2032 sizeof(c->fmt_ctx.comment));
2033 av_strlcpy(c->fmt_ctx.copyright, c->stream->copyright,
2034 sizeof(c->fmt_ctx.copyright));
2035 av_strlcpy(c->fmt_ctx.title, c->stream->title,
2036 sizeof(c->fmt_ctx.title));
2038 for(i=0;i<c->stream->nb_streams;i++) {
2039 AVStream *st;
2040 AVStream *src;
2041 st = av_mallocz(sizeof(AVStream));
2042 c->fmt_ctx.streams[i] = st;
2043 /* if file or feed, then just take streams from FFStream struct */
2044 if (!c->stream->feed ||
2045 c->stream->feed == c->stream)
2046 src = c->stream->streams[i];
2047 else
2048 src = c->stream->feed->streams[c->stream->feed_streams[i]];
2050 *st = *src;
2051 st->priv_data = 0;
2052 st->codec->frame_number = 0; /* XXX: should be done in
2053 AVStream, not in codec */
2055 /* set output format parameters */
2056 c->fmt_ctx.oformat = c->stream->fmt;
2057 c->fmt_ctx.nb_streams = c->stream->nb_streams;
2059 c->got_key_frame = 0;
2061 /* prepare header and save header data in a stream */
2062 if (url_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2063 /* XXX: potential leak */
2064 return -1;
2066 c->fmt_ctx.pb->is_streamed = 1;
2069 * HACK to avoid mpeg ps muxer to spit many underflow errors
2070 * Default value from FFmpeg
2071 * Try to set it use configuration option
2073 c->fmt_ctx.preload = (int)(0.5*AV_TIME_BASE);
2074 c->fmt_ctx.max_delay = (int)(0.7*AV_TIME_BASE);
2076 av_set_parameters(&c->fmt_ctx, NULL);
2077 if (av_write_header(&c->fmt_ctx) < 0) {
2078 http_log("Error writing output header\n");
2079 return -1;
2082 len = url_close_dyn_buf(c->fmt_ctx.pb, &c->pb_buffer);
2083 c->buffer_ptr = c->pb_buffer;
2084 c->buffer_end = c->pb_buffer + len;
2086 c->state = HTTPSTATE_SEND_DATA;
2087 c->last_packet_sent = 0;
2088 break;
2089 case HTTPSTATE_SEND_DATA:
2090 /* find a new packet */
2091 /* read a packet from the input stream */
2092 if (c->stream->feed)
2093 ffm_set_write_index(c->fmt_in,
2094 c->stream->feed->feed_write_index,
2095 c->stream->feed->feed_size);
2097 if (c->stream->max_time &&
2098 c->stream->max_time + c->start_time - cur_time < 0)
2099 /* We have timed out */
2100 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2101 else {
2102 AVPacket pkt;
2103 redo:
2104 if (av_read_frame(c->fmt_in, &pkt) < 0) {
2105 if (c->stream->feed && c->stream->feed->feed_opened) {
2106 /* if coming from feed, it means we reached the end of the
2107 ffm file, so must wait for more data */
2108 c->state = HTTPSTATE_WAIT_FEED;
2109 return 1; /* state changed */
2110 } else {
2111 if (c->stream->loop) {
2112 av_close_input_file(c->fmt_in);
2113 c->fmt_in = NULL;
2114 if (open_input_stream(c, "") < 0)
2115 goto no_loop;
2116 goto redo;
2117 } else {
2118 no_loop:
2119 /* must send trailer now because eof or error */
2120 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2123 } else {
2124 int source_index = pkt.stream_index;
2125 /* update first pts if needed */
2126 if (c->first_pts == AV_NOPTS_VALUE) {
2127 c->first_pts = av_rescale_q(pkt.dts, c->fmt_in->streams[pkt.stream_index]->time_base, AV_TIME_BASE_Q);
2128 c->start_time = cur_time;
2130 /* send it to the appropriate stream */
2131 if (c->stream->feed) {
2132 /* if coming from a feed, select the right stream */
2133 if (c->switch_pending) {
2134 c->switch_pending = 0;
2135 for(i=0;i<c->stream->nb_streams;i++) {
2136 if (c->switch_feed_streams[i] == pkt.stream_index)
2137 if (pkt.flags & PKT_FLAG_KEY)
2138 do_switch_stream(c, i);
2139 if (c->switch_feed_streams[i] >= 0)
2140 c->switch_pending = 1;
2143 for(i=0;i<c->stream->nb_streams;i++) {
2144 if (c->feed_streams[i] == pkt.stream_index) {
2145 AVStream *st = c->fmt_in->streams[source_index];
2146 pkt.stream_index = i;
2147 if (pkt.flags & PKT_FLAG_KEY &&
2148 (st->codec->codec_type == CODEC_TYPE_VIDEO ||
2149 c->stream->nb_streams == 1))
2150 c->got_key_frame = 1;
2151 if (!c->stream->send_on_key || c->got_key_frame)
2152 goto send_it;
2155 } else {
2156 AVCodecContext *codec;
2157 AVStream *ist, *ost;
2158 send_it:
2159 ist = c->fmt_in->streams[source_index];
2160 /* specific handling for RTP: we use several
2161 output stream (one for each RTP
2162 connection). XXX: need more abstract handling */
2163 if (c->is_packetized) {
2164 /* compute send time and duration */
2165 c->cur_pts = av_rescale_q(pkt.dts, ist->time_base, AV_TIME_BASE_Q);
2166 if (ist->start_time != AV_NOPTS_VALUE)
2167 c->cur_pts -= av_rescale_q(ist->start_time, ist->time_base, AV_TIME_BASE_Q);
2168 c->cur_frame_duration = av_rescale_q(pkt.duration, ist->time_base, AV_TIME_BASE_Q);
2169 #if 0
2170 printf("index=%d pts=%0.3f duration=%0.6f\n",
2171 pkt.stream_index,
2172 (double)c->cur_pts /
2173 AV_TIME_BASE,
2174 (double)c->cur_frame_duration /
2175 AV_TIME_BASE);
2176 #endif
2177 /* find RTP context */
2178 c->packet_stream_index = pkt.stream_index;
2179 ctx = c->rtp_ctx[c->packet_stream_index];
2180 if(!ctx) {
2181 av_free_packet(&pkt);
2182 break;
2184 codec = ctx->streams[0]->codec;
2185 /* only one stream per RTP connection */
2186 pkt.stream_index = 0;
2187 } else {
2188 ctx = &c->fmt_ctx;
2189 /* Fudge here */
2190 codec = ctx->streams[pkt.stream_index]->codec;
2193 if (c->is_packetized) {
2194 int max_packet_size;
2195 if (c->rtp_protocol == RTSP_PROTOCOL_RTP_TCP)
2196 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
2197 else
2198 max_packet_size = url_get_max_packet_size(c->rtp_handles[c->packet_stream_index]);
2199 ret = url_open_dyn_packet_buf(&ctx->pb, max_packet_size);
2200 } else {
2201 ret = url_open_dyn_buf(&ctx->pb);
2203 if (ret < 0) {
2204 /* XXX: potential leak */
2205 return -1;
2207 ost = ctx->streams[pkt.stream_index];
2209 ctx->pb->is_streamed = 1;
2210 if (pkt.dts != AV_NOPTS_VALUE)
2211 pkt.dts = av_rescale_q(pkt.dts, ist->time_base, ost->time_base);
2212 if (pkt.pts != AV_NOPTS_VALUE)
2213 pkt.pts = av_rescale_q(pkt.pts, ist->time_base, ost->time_base);
2214 pkt.duration = av_rescale_q(pkt.duration, ist->time_base, ost->time_base);
2215 if (av_write_frame(ctx, &pkt) < 0) {
2216 http_log("Error writing frame to output\n");
2217 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2220 len = url_close_dyn_buf(ctx->pb, &c->pb_buffer);
2221 c->cur_frame_bytes = len;
2222 c->buffer_ptr = c->pb_buffer;
2223 c->buffer_end = c->pb_buffer + len;
2225 codec->frame_number++;
2226 if (len == 0) {
2227 av_free_packet(&pkt);
2228 goto redo;
2231 av_free_packet(&pkt);
2234 break;
2235 default:
2236 case HTTPSTATE_SEND_DATA_TRAILER:
2237 /* last packet test ? */
2238 if (c->last_packet_sent || c->is_packetized)
2239 return -1;
2240 ctx = &c->fmt_ctx;
2241 /* prepare header */
2242 if (url_open_dyn_buf(&ctx->pb) < 0) {
2243 /* XXX: potential leak */
2244 return -1;
2246 c->fmt_ctx.pb->is_streamed = 1;
2247 av_write_trailer(ctx);
2248 len = url_close_dyn_buf(ctx->pb, &c->pb_buffer);
2249 c->buffer_ptr = c->pb_buffer;
2250 c->buffer_end = c->pb_buffer + len;
2252 c->last_packet_sent = 1;
2253 break;
2255 return 0;
2258 /* should convert the format at the same time */
2259 /* send data starting at c->buffer_ptr to the output connection
2260 (either UDP or TCP connection) */
2261 static int http_send_data(HTTPContext *c)
2263 int len, ret;
2265 for(;;) {
2266 if (c->buffer_ptr >= c->buffer_end) {
2267 ret = http_prepare_data(c);
2268 if (ret < 0)
2269 return -1;
2270 else if (ret != 0)
2271 /* state change requested */
2272 break;
2273 } else {
2274 if (c->is_packetized) {
2275 /* RTP data output */
2276 len = c->buffer_end - c->buffer_ptr;
2277 if (len < 4) {
2278 /* fail safe - should never happen */
2279 fail1:
2280 c->buffer_ptr = c->buffer_end;
2281 return 0;
2283 len = (c->buffer_ptr[0] << 24) |
2284 (c->buffer_ptr[1] << 16) |
2285 (c->buffer_ptr[2] << 8) |
2286 (c->buffer_ptr[3]);
2287 if (len > (c->buffer_end - c->buffer_ptr))
2288 goto fail1;
2289 if ((get_packet_send_clock(c) - get_server_clock(c)) > 0) {
2290 /* nothing to send yet: we can wait */
2291 return 0;
2294 c->data_count += len;
2295 update_datarate(&c->datarate, c->data_count);
2296 if (c->stream)
2297 c->stream->bytes_served += len;
2299 if (c->rtp_protocol == RTSP_PROTOCOL_RTP_TCP) {
2300 /* RTP packets are sent inside the RTSP TCP connection */
2301 ByteIOContext *pb;
2302 int interleaved_index, size;
2303 uint8_t header[4];
2304 HTTPContext *rtsp_c;
2306 rtsp_c = c->rtsp_c;
2307 /* if no RTSP connection left, error */
2308 if (!rtsp_c)
2309 return -1;
2310 /* if already sending something, then wait. */
2311 if (rtsp_c->state != RTSPSTATE_WAIT_REQUEST)
2312 break;
2313 if (url_open_dyn_buf(&pb) < 0)
2314 goto fail1;
2315 interleaved_index = c->packet_stream_index * 2;
2316 /* RTCP packets are sent at odd indexes */
2317 if (c->buffer_ptr[1] == 200)
2318 interleaved_index++;
2319 /* write RTSP TCP header */
2320 header[0] = '$';
2321 header[1] = interleaved_index;
2322 header[2] = len >> 8;
2323 header[3] = len;
2324 put_buffer(pb, header, 4);
2325 /* write RTP packet data */
2326 c->buffer_ptr += 4;
2327 put_buffer(pb, c->buffer_ptr, len);
2328 size = url_close_dyn_buf(pb, &c->packet_buffer);
2329 /* prepare asynchronous TCP sending */
2330 rtsp_c->packet_buffer_ptr = c->packet_buffer;
2331 rtsp_c->packet_buffer_end = c->packet_buffer + size;
2332 c->buffer_ptr += len;
2334 /* send everything we can NOW */
2335 len = send(rtsp_c->fd, rtsp_c->packet_buffer_ptr,
2336 rtsp_c->packet_buffer_end - rtsp_c->packet_buffer_ptr, 0);
2337 if (len > 0)
2338 rtsp_c->packet_buffer_ptr += len;
2339 if (rtsp_c->packet_buffer_ptr < rtsp_c->packet_buffer_end) {
2340 /* if we could not send all the data, we will
2341 send it later, so a new state is needed to
2342 "lock" the RTSP TCP connection */
2343 rtsp_c->state = RTSPSTATE_SEND_PACKET;
2344 break;
2345 } else
2346 /* all data has been sent */
2347 av_freep(&c->packet_buffer);
2348 } else {
2349 /* send RTP packet directly in UDP */
2350 c->buffer_ptr += 4;
2351 url_write(c->rtp_handles[c->packet_stream_index],
2352 c->buffer_ptr, len);
2353 c->buffer_ptr += len;
2354 /* here we continue as we can send several packets per 10 ms slot */
2356 } else {
2357 /* TCP data output */
2358 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2359 if (len < 0) {
2360 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2361 ff_neterrno() != FF_NETERROR(EINTR))
2362 /* error : close connection */
2363 return -1;
2364 else
2365 return 0;
2366 } else
2367 c->buffer_ptr += len;
2369 c->data_count += len;
2370 update_datarate(&c->datarate, c->data_count);
2371 if (c->stream)
2372 c->stream->bytes_served += len;
2373 break;
2376 } /* for(;;) */
2377 return 0;
2380 static int http_start_receive_data(HTTPContext *c)
2382 int fd;
2384 if (c->stream->feed_opened)
2385 return -1;
2387 /* Don't permit writing to this one */
2388 if (c->stream->readonly)
2389 return -1;
2391 /* open feed */
2392 fd = open(c->stream->feed_filename, O_RDWR);
2393 if (fd < 0) {
2394 http_log("Error opening feeder file: %s\n", strerror(errno));
2395 return -1;
2397 c->feed_fd = fd;
2399 c->stream->feed_write_index = ffm_read_write_index(fd);
2400 c->stream->feed_size = lseek(fd, 0, SEEK_END);
2401 lseek(fd, 0, SEEK_SET);
2403 /* init buffer input */
2404 c->buffer_ptr = c->buffer;
2405 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2406 c->stream->feed_opened = 1;
2407 return 0;
2410 static int http_receive_data(HTTPContext *c)
2412 HTTPContext *c1;
2414 if (c->buffer_end > c->buffer_ptr) {
2415 int len;
2417 len = recv(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2418 if (len < 0) {
2419 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2420 ff_neterrno() != FF_NETERROR(EINTR))
2421 /* error : close connection */
2422 goto fail;
2423 } else if (len == 0)
2424 /* end of connection : close it */
2425 goto fail;
2426 else {
2427 c->buffer_ptr += len;
2428 c->data_count += len;
2429 update_datarate(&c->datarate, c->data_count);
2433 if (c->buffer_ptr - c->buffer >= 2 && c->data_count > FFM_PACKET_SIZE) {
2434 if (c->buffer[0] != 'f' ||
2435 c->buffer[1] != 'm') {
2436 http_log("Feed stream has become desynchronized -- disconnecting\n");
2437 goto fail;
2441 if (c->buffer_ptr >= c->buffer_end) {
2442 FFStream *feed = c->stream;
2443 /* a packet has been received : write it in the store, except
2444 if header */
2445 if (c->data_count > FFM_PACKET_SIZE) {
2447 // printf("writing pos=0x%"PRIx64" size=0x%"PRIx64"\n", feed->feed_write_index, feed->feed_size);
2448 /* XXX: use llseek or url_seek */
2449 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2450 if (write(c->feed_fd, c->buffer, FFM_PACKET_SIZE) < 0) {
2451 http_log("Error writing to feed file: %s\n", strerror(errno));
2452 goto fail;
2455 feed->feed_write_index += FFM_PACKET_SIZE;
2456 /* update file size */
2457 if (feed->feed_write_index > c->stream->feed_size)
2458 feed->feed_size = feed->feed_write_index;
2460 /* handle wrap around if max file size reached */
2461 if (c->stream->feed_max_size && feed->feed_write_index >= c->stream->feed_max_size)
2462 feed->feed_write_index = FFM_PACKET_SIZE;
2464 /* write index */
2465 ffm_write_write_index(c->feed_fd, feed->feed_write_index);
2467 /* wake up any waiting connections */
2468 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2469 if (c1->state == HTTPSTATE_WAIT_FEED &&
2470 c1->stream->feed == c->stream->feed)
2471 c1->state = HTTPSTATE_SEND_DATA;
2473 } else {
2474 /* We have a header in our hands that contains useful data */
2475 AVFormatContext *s = NULL;
2476 ByteIOContext *pb;
2477 AVInputFormat *fmt_in;
2478 int i;
2480 /* use feed output format name to find corresponding input format */
2481 fmt_in = av_find_input_format(feed->fmt->name);
2482 if (!fmt_in)
2483 goto fail;
2485 url_open_buf(&pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY);
2486 pb->is_streamed = 1;
2488 if (av_open_input_stream(&s, pb, c->stream->feed_filename, fmt_in, NULL) < 0) {
2489 av_free(pb);
2490 goto fail;
2493 /* Now we have the actual streams */
2494 if (s->nb_streams != feed->nb_streams) {
2495 av_close_input_stream(s);
2496 av_free(pb);
2497 goto fail;
2500 for (i = 0; i < s->nb_streams; i++) {
2501 AVStream *fst = feed->streams[i];
2502 AVStream *st = s->streams[i];
2503 memcpy(fst->codec, st->codec, sizeof(AVCodecContext));
2504 if (fst->codec->extradata_size) {
2505 fst->codec->extradata = av_malloc(fst->codec->extradata_size);
2506 if (!fst->codec->extradata)
2507 goto fail;
2508 memcpy(fst->codec->extradata, st->codec->extradata,
2509 fst->codec->extradata_size);
2513 av_close_input_stream(s);
2514 av_free(pb);
2516 c->buffer_ptr = c->buffer;
2519 return 0;
2520 fail:
2521 c->stream->feed_opened = 0;
2522 close(c->feed_fd);
2523 /* wake up any waiting connections to stop waiting for feed */
2524 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2525 if (c1->state == HTTPSTATE_WAIT_FEED &&
2526 c1->stream->feed == c->stream->feed)
2527 c1->state = HTTPSTATE_SEND_DATA_TRAILER;
2529 return -1;
2532 /********************************************************************/
2533 /* RTSP handling */
2535 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2537 const char *str;
2538 time_t ti;
2539 char *p;
2540 char buf2[32];
2542 switch(error_number) {
2543 case RTSP_STATUS_OK:
2544 str = "OK";
2545 break;
2546 case RTSP_STATUS_METHOD:
2547 str = "Method Not Allowed";
2548 break;
2549 case RTSP_STATUS_BANDWIDTH:
2550 str = "Not Enough Bandwidth";
2551 break;
2552 case RTSP_STATUS_SESSION:
2553 str = "Session Not Found";
2554 break;
2555 case RTSP_STATUS_STATE:
2556 str = "Method Not Valid in This State";
2557 break;
2558 case RTSP_STATUS_AGGREGATE:
2559 str = "Aggregate operation not allowed";
2560 break;
2561 case RTSP_STATUS_ONLY_AGGREGATE:
2562 str = "Only aggregate operation allowed";
2563 break;
2564 case RTSP_STATUS_TRANSPORT:
2565 str = "Unsupported transport";
2566 break;
2567 case RTSP_STATUS_INTERNAL:
2568 str = "Internal Server Error";
2569 break;
2570 case RTSP_STATUS_SERVICE:
2571 str = "Service Unavailable";
2572 break;
2573 case RTSP_STATUS_VERSION:
2574 str = "RTSP Version not supported";
2575 break;
2576 default:
2577 str = "Unknown Error";
2578 break;
2581 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
2582 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2584 /* output GMT time */
2585 ti = time(NULL);
2586 p = ctime(&ti);
2587 strcpy(buf2, p);
2588 p = buf2 + strlen(p) - 1;
2589 if (*p == '\n')
2590 *p = '\0';
2591 url_fprintf(c->pb, "Date: %s GMT\r\n", buf2);
2594 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
2596 rtsp_reply_header(c, error_number);
2597 url_fprintf(c->pb, "\r\n");
2600 static int rtsp_parse_request(HTTPContext *c)
2602 const char *p, *p1, *p2;
2603 char cmd[32];
2604 char url[1024];
2605 char protocol[32];
2606 char line[1024];
2607 int len;
2608 RTSPHeader header1, *header = &header1;
2610 c->buffer_ptr[0] = '\0';
2611 p = c->buffer;
2613 get_word(cmd, sizeof(cmd), &p);
2614 get_word(url, sizeof(url), &p);
2615 get_word(protocol, sizeof(protocol), &p);
2617 av_strlcpy(c->method, cmd, sizeof(c->method));
2618 av_strlcpy(c->url, url, sizeof(c->url));
2619 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
2621 if (url_open_dyn_buf(&c->pb) < 0) {
2622 /* XXX: cannot do more */
2623 c->pb = NULL; /* safety */
2624 return -1;
2627 /* check version name */
2628 if (strcmp(protocol, "RTSP/1.0") != 0) {
2629 rtsp_reply_error(c, RTSP_STATUS_VERSION);
2630 goto the_end;
2633 /* parse each header line */
2634 memset(header, 0, sizeof(RTSPHeader));
2635 /* skip to next line */
2636 while (*p != '\n' && *p != '\0')
2637 p++;
2638 if (*p == '\n')
2639 p++;
2640 while (*p != '\0') {
2641 p1 = strchr(p, '\n');
2642 if (!p1)
2643 break;
2644 p2 = p1;
2645 if (p2 > p && p2[-1] == '\r')
2646 p2--;
2647 /* skip empty line */
2648 if (p2 == p)
2649 break;
2650 len = p2 - p;
2651 if (len > sizeof(line) - 1)
2652 len = sizeof(line) - 1;
2653 memcpy(line, p, len);
2654 line[len] = '\0';
2655 rtsp_parse_line(header, line);
2656 p = p1 + 1;
2659 /* handle sequence number */
2660 c->seq = header->seq;
2662 if (!strcmp(cmd, "DESCRIBE"))
2663 rtsp_cmd_describe(c, url);
2664 else if (!strcmp(cmd, "OPTIONS"))
2665 rtsp_cmd_options(c, url);
2666 else if (!strcmp(cmd, "SETUP"))
2667 rtsp_cmd_setup(c, url, header);
2668 else if (!strcmp(cmd, "PLAY"))
2669 rtsp_cmd_play(c, url, header);
2670 else if (!strcmp(cmd, "PAUSE"))
2671 rtsp_cmd_pause(c, url, header);
2672 else if (!strcmp(cmd, "TEARDOWN"))
2673 rtsp_cmd_teardown(c, url, header);
2674 else
2675 rtsp_reply_error(c, RTSP_STATUS_METHOD);
2677 the_end:
2678 len = url_close_dyn_buf(c->pb, &c->pb_buffer);
2679 c->pb = NULL; /* safety */
2680 if (len < 0) {
2681 /* XXX: cannot do more */
2682 return -1;
2684 c->buffer_ptr = c->pb_buffer;
2685 c->buffer_end = c->pb_buffer + len;
2686 c->state = RTSPSTATE_SEND_REPLY;
2687 return 0;
2690 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
2691 struct in_addr my_ip)
2693 AVFormatContext *avc;
2694 AVStream avs[MAX_STREAMS];
2695 int i;
2697 avc = av_alloc_format_context();
2698 if (avc == NULL) {
2699 return -1;
2701 if (stream->title[0] != 0) {
2702 av_strlcpy(avc->title, stream->title, sizeof(avc->title));
2703 } else {
2704 av_strlcpy(avc->title, "No Title", sizeof(avc->title));
2706 avc->nb_streams = stream->nb_streams;
2707 if (stream->is_multicast) {
2708 snprintf(avc->filename, 1024, "rtp://%s:%d?multicast=1?ttl=%d",
2709 inet_ntoa(stream->multicast_ip),
2710 stream->multicast_port, stream->multicast_ttl);
2713 for(i = 0; i < stream->nb_streams; i++) {
2714 avc->streams[i] = &avs[i];
2715 avc->streams[i]->codec = stream->streams[i]->codec;
2717 *pbuffer = av_mallocz(2048);
2718 avf_sdp_create(&avc, 1, *pbuffer, 2048);
2719 av_free(avc);
2721 return strlen(*pbuffer);
2724 static void rtsp_cmd_options(HTTPContext *c, const char *url)
2726 // rtsp_reply_header(c, RTSP_STATUS_OK);
2727 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK");
2728 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2729 url_fprintf(c->pb, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
2730 url_fprintf(c->pb, "\r\n");
2733 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
2735 FFStream *stream;
2736 char path1[1024];
2737 const char *path;
2738 uint8_t *content;
2739 int content_length, len;
2740 struct sockaddr_in my_addr;
2742 /* find which url is asked */
2743 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2744 path = path1;
2745 if (*path == '/')
2746 path++;
2748 for(stream = first_stream; stream != NULL; stream = stream->next) {
2749 if (!stream->is_feed &&
2750 stream->fmt && !strcmp(stream->fmt->name, "rtp") &&
2751 !strcmp(path, stream->filename)) {
2752 goto found;
2755 /* no stream found */
2756 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2757 return;
2759 found:
2760 /* prepare the media description in sdp format */
2762 /* get the host IP */
2763 len = sizeof(my_addr);
2764 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
2765 content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
2766 if (content_length < 0) {
2767 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2768 return;
2770 rtsp_reply_header(c, RTSP_STATUS_OK);
2771 url_fprintf(c->pb, "Content-Type: application/sdp\r\n");
2772 url_fprintf(c->pb, "Content-Length: %d\r\n", content_length);
2773 url_fprintf(c->pb, "\r\n");
2774 put_buffer(c->pb, content, content_length);
2777 static HTTPContext *find_rtp_session(const char *session_id)
2779 HTTPContext *c;
2781 if (session_id[0] == '\0')
2782 return NULL;
2784 for(c = first_http_ctx; c != NULL; c = c->next) {
2785 if (!strcmp(c->session_id, session_id))
2786 return c;
2788 return NULL;
2791 static RTSPTransportField *find_transport(RTSPHeader *h, enum RTSPProtocol protocol)
2793 RTSPTransportField *th;
2794 int i;
2796 for(i=0;i<h->nb_transports;i++) {
2797 th = &h->transports[i];
2798 if (th->protocol == protocol)
2799 return th;
2801 return NULL;
2804 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
2805 RTSPHeader *h)
2807 FFStream *stream;
2808 int stream_index, port;
2809 char buf[1024];
2810 char path1[1024];
2811 const char *path;
2812 HTTPContext *rtp_c;
2813 RTSPTransportField *th;
2814 struct sockaddr_in dest_addr;
2815 RTSPActionServerSetup setup;
2817 /* find which url is asked */
2818 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2819 path = path1;
2820 if (*path == '/')
2821 path++;
2823 /* now check each stream */
2824 for(stream = first_stream; stream != NULL; stream = stream->next) {
2825 if (!stream->is_feed &&
2826 stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
2827 /* accept aggregate filenames only if single stream */
2828 if (!strcmp(path, stream->filename)) {
2829 if (stream->nb_streams != 1) {
2830 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
2831 return;
2833 stream_index = 0;
2834 goto found;
2837 for(stream_index = 0; stream_index < stream->nb_streams;
2838 stream_index++) {
2839 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2840 stream->filename, stream_index);
2841 if (!strcmp(path, buf))
2842 goto found;
2846 /* no stream found */
2847 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2848 return;
2849 found:
2851 /* generate session id if needed */
2852 if (h->session_id[0] == '\0')
2853 snprintf(h->session_id, sizeof(h->session_id), "%08x%08x",
2854 av_random(&random_state), av_random(&random_state));
2856 /* find rtp session, and create it if none found */
2857 rtp_c = find_rtp_session(h->session_id);
2858 if (!rtp_c) {
2859 /* always prefer UDP */
2860 th = find_transport(h, RTSP_PROTOCOL_RTP_UDP);
2861 if (!th) {
2862 th = find_transport(h, RTSP_PROTOCOL_RTP_TCP);
2863 if (!th) {
2864 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2865 return;
2869 rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id,
2870 th->protocol);
2871 if (!rtp_c) {
2872 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
2873 return;
2876 /* open input stream */
2877 if (open_input_stream(rtp_c, "") < 0) {
2878 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2879 return;
2883 /* test if stream is OK (test needed because several SETUP needs
2884 to be done for a given file) */
2885 if (rtp_c->stream != stream) {
2886 rtsp_reply_error(c, RTSP_STATUS_SERVICE);
2887 return;
2890 /* test if stream is already set up */
2891 if (rtp_c->rtp_ctx[stream_index]) {
2892 rtsp_reply_error(c, RTSP_STATUS_STATE);
2893 return;
2896 /* check transport */
2897 th = find_transport(h, rtp_c->rtp_protocol);
2898 if (!th || (th->protocol == RTSP_PROTOCOL_RTP_UDP &&
2899 th->client_port_min <= 0)) {
2900 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2901 return;
2904 /* setup default options */
2905 setup.transport_option[0] = '\0';
2906 dest_addr = rtp_c->from_addr;
2907 dest_addr.sin_port = htons(th->client_port_min);
2909 /* setup stream */
2910 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) {
2911 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2912 return;
2915 /* now everything is OK, so we can send the connection parameters */
2916 rtsp_reply_header(c, RTSP_STATUS_OK);
2917 /* session ID */
2918 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2920 switch(rtp_c->rtp_protocol) {
2921 case RTSP_PROTOCOL_RTP_UDP:
2922 port = rtp_get_local_port(rtp_c->rtp_handles[stream_index]);
2923 url_fprintf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
2924 "client_port=%d-%d;server_port=%d-%d",
2925 th->client_port_min, th->client_port_min + 1,
2926 port, port + 1);
2927 break;
2928 case RTSP_PROTOCOL_RTP_TCP:
2929 url_fprintf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
2930 stream_index * 2, stream_index * 2 + 1);
2931 break;
2932 default:
2933 break;
2935 if (setup.transport_option[0] != '\0')
2936 url_fprintf(c->pb, ";%s", setup.transport_option);
2937 url_fprintf(c->pb, "\r\n");
2940 url_fprintf(c->pb, "\r\n");
2944 /* find an rtp connection by using the session ID. Check consistency
2945 with filename */
2946 static HTTPContext *find_rtp_session_with_url(const char *url,
2947 const char *session_id)
2949 HTTPContext *rtp_c;
2950 char path1[1024];
2951 const char *path;
2952 char buf[1024];
2953 int s;
2955 rtp_c = find_rtp_session(session_id);
2956 if (!rtp_c)
2957 return NULL;
2959 /* find which url is asked */
2960 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2961 path = path1;
2962 if (*path == '/')
2963 path++;
2964 if(!strcmp(path, rtp_c->stream->filename)) return rtp_c;
2965 for(s=0; s<rtp_c->stream->nb_streams; ++s) {
2966 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2967 rtp_c->stream->filename, s);
2968 if(!strncmp(path, buf, sizeof(buf))) {
2969 // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
2970 return rtp_c;
2973 return NULL;
2976 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h)
2978 HTTPContext *rtp_c;
2980 rtp_c = find_rtp_session_with_url(url, h->session_id);
2981 if (!rtp_c) {
2982 rtsp_reply_error(c, RTSP_STATUS_SESSION);
2983 return;
2986 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
2987 rtp_c->state != HTTPSTATE_WAIT_FEED &&
2988 rtp_c->state != HTTPSTATE_READY) {
2989 rtsp_reply_error(c, RTSP_STATUS_STATE);
2990 return;
2993 #if 0
2994 /* XXX: seek in stream */
2995 if (h->range_start != AV_NOPTS_VALUE) {
2996 printf("range_start=%0.3f\n", (double)h->range_start / AV_TIME_BASE);
2997 av_seek_frame(rtp_c->fmt_in, -1, h->range_start);
2999 #endif
3001 rtp_c->state = HTTPSTATE_SEND_DATA;
3003 /* now everything is OK, so we can send the connection parameters */
3004 rtsp_reply_header(c, RTSP_STATUS_OK);
3005 /* session ID */
3006 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3007 url_fprintf(c->pb, "\r\n");
3010 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h)
3012 HTTPContext *rtp_c;
3014 rtp_c = find_rtp_session_with_url(url, h->session_id);
3015 if (!rtp_c) {
3016 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3017 return;
3020 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3021 rtp_c->state != HTTPSTATE_WAIT_FEED) {
3022 rtsp_reply_error(c, RTSP_STATUS_STATE);
3023 return;
3026 rtp_c->state = HTTPSTATE_READY;
3027 rtp_c->first_pts = AV_NOPTS_VALUE;
3028 /* now everything is OK, so we can send the connection parameters */
3029 rtsp_reply_header(c, RTSP_STATUS_OK);
3030 /* session ID */
3031 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3032 url_fprintf(c->pb, "\r\n");
3035 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h)
3037 HTTPContext *rtp_c;
3038 char session_id[32];
3040 rtp_c = find_rtp_session_with_url(url, h->session_id);
3041 if (!rtp_c) {
3042 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3043 return;
3046 av_strlcpy(session_id, rtp_c->session_id, sizeof(session_id));
3048 /* abort the session */
3049 close_connection(rtp_c);
3051 /* now everything is OK, so we can send the connection parameters */
3052 rtsp_reply_header(c, RTSP_STATUS_OK);
3053 /* session ID */
3054 url_fprintf(c->pb, "Session: %s\r\n", session_id);
3055 url_fprintf(c->pb, "\r\n");
3059 /********************************************************************/
3060 /* RTP handling */
3062 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
3063 FFStream *stream, const char *session_id,
3064 enum RTSPProtocol rtp_protocol)
3066 HTTPContext *c = NULL;
3067 const char *proto_str;
3069 /* XXX: should output a warning page when coming
3070 close to the connection limit */
3071 if (nb_connections >= nb_max_connections)
3072 goto fail;
3074 /* add a new connection */
3075 c = av_mallocz(sizeof(HTTPContext));
3076 if (!c)
3077 goto fail;
3079 c->fd = -1;
3080 c->poll_entry = NULL;
3081 c->from_addr = *from_addr;
3082 c->buffer_size = IOBUFFER_INIT_SIZE;
3083 c->buffer = av_malloc(c->buffer_size);
3084 if (!c->buffer)
3085 goto fail;
3086 nb_connections++;
3087 c->stream = stream;
3088 av_strlcpy(c->session_id, session_id, sizeof(c->session_id));
3089 c->state = HTTPSTATE_READY;
3090 c->is_packetized = 1;
3091 c->rtp_protocol = rtp_protocol;
3093 /* protocol is shown in statistics */
3094 switch(c->rtp_protocol) {
3095 case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
3096 proto_str = "MCAST";
3097 break;
3098 case RTSP_PROTOCOL_RTP_UDP:
3099 proto_str = "UDP";
3100 break;
3101 case RTSP_PROTOCOL_RTP_TCP:
3102 proto_str = "TCP";
3103 break;
3104 default:
3105 proto_str = "???";
3106 break;
3108 av_strlcpy(c->protocol, "RTP/", sizeof(c->protocol));
3109 av_strlcat(c->protocol, proto_str, sizeof(c->protocol));
3111 current_bandwidth += stream->bandwidth;
3113 c->next = first_http_ctx;
3114 first_http_ctx = c;
3115 return c;
3117 fail:
3118 if (c) {
3119 av_free(c->buffer);
3120 av_free(c);
3122 return NULL;
3125 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3126 command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
3127 used. */
3128 static int rtp_new_av_stream(HTTPContext *c,
3129 int stream_index, struct sockaddr_in *dest_addr,
3130 HTTPContext *rtsp_c)
3132 AVFormatContext *ctx;
3133 AVStream *st;
3134 char *ipaddr;
3135 URLContext *h = NULL;
3136 uint8_t *dummy_buf;
3137 int max_packet_size;
3139 /* now we can open the relevant output stream */
3140 ctx = av_alloc_format_context();
3141 if (!ctx)
3142 return -1;
3143 ctx->oformat = guess_format("rtp", NULL, NULL);
3145 st = av_mallocz(sizeof(AVStream));
3146 if (!st)
3147 goto fail;
3148 st->codec= avcodec_alloc_context();
3149 ctx->nb_streams = 1;
3150 ctx->streams[0] = st;
3152 if (!c->stream->feed ||
3153 c->stream->feed == c->stream)
3154 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3155 else
3156 memcpy(st,
3157 c->stream->feed->streams[c->stream->feed_streams[stream_index]],
3158 sizeof(AVStream));
3159 st->priv_data = NULL;
3161 /* build destination RTP address */
3162 ipaddr = inet_ntoa(dest_addr->sin_addr);
3164 switch(c->rtp_protocol) {
3165 case RTSP_PROTOCOL_RTP_UDP:
3166 case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
3167 /* RTP/UDP case */
3169 /* XXX: also pass as parameter to function ? */
3170 if (c->stream->is_multicast) {
3171 int ttl;
3172 ttl = c->stream->multicast_ttl;
3173 if (!ttl)
3174 ttl = 16;
3175 snprintf(ctx->filename, sizeof(ctx->filename),
3176 "rtp://%s:%d?multicast=1&ttl=%d",
3177 ipaddr, ntohs(dest_addr->sin_port), ttl);
3178 } else {
3179 snprintf(ctx->filename, sizeof(ctx->filename),
3180 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
3183 if (url_open(&h, ctx->filename, URL_WRONLY) < 0)
3184 goto fail;
3185 c->rtp_handles[stream_index] = h;
3186 max_packet_size = url_get_max_packet_size(h);
3187 break;
3188 case RTSP_PROTOCOL_RTP_TCP:
3189 /* RTP/TCP case */
3190 c->rtsp_c = rtsp_c;
3191 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
3192 break;
3193 default:
3194 goto fail;
3197 http_log("%s:%d - - \"PLAY %s/streamid=%d %s\"\n",
3198 ipaddr, ntohs(dest_addr->sin_port),
3199 c->stream->filename, stream_index, c->protocol);
3201 /* normally, no packets should be output here, but the packet size may be checked */
3202 if (url_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) {
3203 /* XXX: close stream */
3204 goto fail;
3206 av_set_parameters(ctx, NULL);
3207 if (av_write_header(ctx) < 0) {
3208 fail:
3209 if (h)
3210 url_close(h);
3211 av_free(ctx);
3212 return -1;
3214 url_close_dyn_buf(ctx->pb, &dummy_buf);
3215 av_free(dummy_buf);
3217 c->rtp_ctx[stream_index] = ctx;
3218 return 0;
3221 /********************************************************************/
3222 /* ffserver initialization */
3224 static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec)
3226 AVStream *fst;
3228 fst = av_mallocz(sizeof(AVStream));
3229 if (!fst)
3230 return NULL;
3231 fst->codec= avcodec_alloc_context();
3232 fst->priv_data = av_mallocz(sizeof(FeedData));
3233 memcpy(fst->codec, codec, sizeof(AVCodecContext));
3234 fst->index = stream->nb_streams;
3235 av_set_pts_info(fst, 33, 1, 90000);
3236 stream->streams[stream->nb_streams++] = fst;
3237 return fst;
3240 /* return the stream number in the feed */
3241 static int add_av_stream(FFStream *feed, AVStream *st)
3243 AVStream *fst;
3244 AVCodecContext *av, *av1;
3245 int i;
3247 av = st->codec;
3248 for(i=0;i<feed->nb_streams;i++) {
3249 st = feed->streams[i];
3250 av1 = st->codec;
3251 if (av1->codec_id == av->codec_id &&
3252 av1->codec_type == av->codec_type &&
3253 av1->bit_rate == av->bit_rate) {
3255 switch(av->codec_type) {
3256 case CODEC_TYPE_AUDIO:
3257 if (av1->channels == av->channels &&
3258 av1->sample_rate == av->sample_rate)
3259 goto found;
3260 break;
3261 case CODEC_TYPE_VIDEO:
3262 if (av1->width == av->width &&
3263 av1->height == av->height &&
3264 av1->time_base.den == av->time_base.den &&
3265 av1->time_base.num == av->time_base.num &&
3266 av1->gop_size == av->gop_size)
3267 goto found;
3268 break;
3269 default:
3270 abort();
3275 fst = add_av_stream1(feed, av);
3276 if (!fst)
3277 return -1;
3278 return feed->nb_streams - 1;
3279 found:
3280 return i;
3283 static void remove_stream(FFStream *stream)
3285 FFStream **ps;
3286 ps = &first_stream;
3287 while (*ps != NULL) {
3288 if (*ps == stream)
3289 *ps = (*ps)->next;
3290 else
3291 ps = &(*ps)->next;
3295 /* specific mpeg4 handling : we extract the raw parameters */
3296 static void extract_mpeg4_header(AVFormatContext *infile)
3298 int mpeg4_count, i, size;
3299 AVPacket pkt;
3300 AVStream *st;
3301 const uint8_t *p;
3303 mpeg4_count = 0;
3304 for(i=0;i<infile->nb_streams;i++) {
3305 st = infile->streams[i];
3306 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3307 st->codec->extradata_size == 0) {
3308 mpeg4_count++;
3311 if (!mpeg4_count)
3312 return;
3314 printf("MPEG4 without extra data: trying to find header in %s\n", infile->filename);
3315 while (mpeg4_count > 0) {
3316 if (av_read_packet(infile, &pkt) < 0)
3317 break;
3318 st = infile->streams[pkt.stream_index];
3319 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3320 st->codec->extradata_size == 0) {
3321 av_freep(&st->codec->extradata);
3322 /* fill extradata with the header */
3323 /* XXX: we make hard suppositions here ! */
3324 p = pkt.data;
3325 while (p < pkt.data + pkt.size - 4) {
3326 /* stop when vop header is found */
3327 if (p[0] == 0x00 && p[1] == 0x00 &&
3328 p[2] == 0x01 && p[3] == 0xb6) {
3329 size = p - pkt.data;
3330 // av_hex_dump_log(infile, AV_LOG_DEBUG, pkt.data, size);
3331 st->codec->extradata = av_malloc(size);
3332 st->codec->extradata_size = size;
3333 memcpy(st->codec->extradata, pkt.data, size);
3334 break;
3336 p++;
3338 mpeg4_count--;
3340 av_free_packet(&pkt);
3344 /* compute the needed AVStream for each file */
3345 static void build_file_streams(void)
3347 FFStream *stream, *stream_next;
3348 AVFormatContext *infile;
3349 int i, ret;
3351 /* gather all streams */
3352 for(stream = first_stream; stream != NULL; stream = stream_next) {
3353 stream_next = stream->next;
3354 if (stream->stream_type == STREAM_TYPE_LIVE &&
3355 !stream->feed) {
3356 /* the stream comes from a file */
3357 /* try to open the file */
3358 /* open stream */
3359 stream->ap_in = av_mallocz(sizeof(AVFormatParameters));
3360 if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
3361 /* specific case : if transport stream output to RTP,
3362 we use a raw transport stream reader */
3363 stream->ap_in->mpeg2ts_raw = 1;
3364 stream->ap_in->mpeg2ts_compute_pcr = 1;
3367 if ((ret = av_open_input_file(&infile, stream->feed_filename,
3368 stream->ifmt, 0, stream->ap_in)) < 0) {
3369 http_log("could not open %s: %d\n", stream->feed_filename, ret);
3370 /* remove stream (no need to spend more time on it) */
3371 fail:
3372 remove_stream(stream);
3373 } else {
3374 /* find all the AVStreams inside and reference them in
3375 'stream' */
3376 if (av_find_stream_info(infile) < 0) {
3377 http_log("Could not find codec parameters from '%s'\n",
3378 stream->feed_filename);
3379 av_close_input_file(infile);
3380 goto fail;
3382 extract_mpeg4_header(infile);
3384 for(i=0;i<infile->nb_streams;i++)
3385 add_av_stream1(stream, infile->streams[i]->codec);
3387 av_close_input_file(infile);
3393 /* compute the needed AVStream for each feed */
3394 static void build_feed_streams(void)
3396 FFStream *stream, *feed;
3397 int i;
3399 /* gather all streams */
3400 for(stream = first_stream; stream != NULL; stream = stream->next) {
3401 feed = stream->feed;
3402 if (feed) {
3403 if (!stream->is_feed) {
3404 /* we handle a stream coming from a feed */
3405 for(i=0;i<stream->nb_streams;i++)
3406 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3411 /* gather all streams */
3412 for(stream = first_stream; stream != NULL; stream = stream->next) {
3413 feed = stream->feed;
3414 if (feed) {
3415 if (stream->is_feed) {
3416 for(i=0;i<stream->nb_streams;i++)
3417 stream->feed_streams[i] = i;
3422 /* create feed files if needed */
3423 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
3424 int fd;
3426 if (url_exist(feed->feed_filename)) {
3427 /* See if it matches */
3428 AVFormatContext *s;
3429 int matches = 0;
3431 if (av_open_input_file(&s, feed->feed_filename, NULL, FFM_PACKET_SIZE, NULL) >= 0) {
3432 /* Now see if it matches */
3433 if (s->nb_streams == feed->nb_streams) {
3434 matches = 1;
3435 for(i=0;i<s->nb_streams;i++) {
3436 AVStream *sf, *ss;
3437 sf = feed->streams[i];
3438 ss = s->streams[i];
3440 if (sf->index != ss->index ||
3441 sf->id != ss->id) {
3442 printf("Index & Id do not match for stream %d (%s)\n",
3443 i, feed->feed_filename);
3444 matches = 0;
3445 } else {
3446 AVCodecContext *ccf, *ccs;
3448 ccf = sf->codec;
3449 ccs = ss->codec;
3450 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3452 if (CHECK_CODEC(codec) || CHECK_CODEC(codec_type)) {
3453 printf("Codecs do not match for stream %d\n", i);
3454 matches = 0;
3455 } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3456 printf("Codec bitrates do not match for stream %d\n", i);
3457 matches = 0;
3458 } else if (ccf->codec_type == CODEC_TYPE_VIDEO) {
3459 if (CHECK_CODEC(time_base.den) ||
3460 CHECK_CODEC(time_base.num) ||
3461 CHECK_CODEC(width) ||
3462 CHECK_CODEC(height)) {
3463 printf("Codec width, height and framerate do not match for stream %d\n", i);
3464 matches = 0;
3466 } else if (ccf->codec_type == CODEC_TYPE_AUDIO) {
3467 if (CHECK_CODEC(sample_rate) ||
3468 CHECK_CODEC(channels) ||
3469 CHECK_CODEC(frame_size)) {
3470 printf("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3471 matches = 0;
3473 } else {
3474 printf("Unknown codec type\n");
3475 matches = 0;
3478 if (!matches)
3479 break;
3481 } else
3482 printf("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3483 feed->feed_filename, s->nb_streams, feed->nb_streams);
3485 av_close_input_file(s);
3486 } else
3487 printf("Deleting feed file '%s' as it appears to be corrupt\n",
3488 feed->feed_filename);
3490 if (!matches) {
3491 if (feed->readonly) {
3492 printf("Unable to delete feed file '%s' as it is marked readonly\n",
3493 feed->feed_filename);
3494 exit(1);
3496 unlink(feed->feed_filename);
3499 if (!url_exist(feed->feed_filename)) {
3500 AVFormatContext s1, *s = &s1;
3502 if (feed->readonly) {
3503 printf("Unable to create feed file '%s' as it is marked readonly\n",
3504 feed->feed_filename);
3505 exit(1);
3508 /* only write the header of the ffm file */
3509 if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
3510 http_log("Could not open output feed file '%s'\n",
3511 feed->feed_filename);
3512 exit(1);
3514 s->oformat = feed->fmt;
3515 s->nb_streams = feed->nb_streams;
3516 for(i=0;i<s->nb_streams;i++) {
3517 AVStream *st;
3518 st = feed->streams[i];
3519 s->streams[i] = st;
3521 av_set_parameters(s, NULL);
3522 if (av_write_header(s) < 0) {
3523 http_log("Container doesn't supports the required parameters\n");
3524 exit(1);
3526 /* XXX: need better api */
3527 av_freep(&s->priv_data);
3528 url_fclose(s->pb);
3530 /* get feed size and write index */
3531 fd = open(feed->feed_filename, O_RDONLY);
3532 if (fd < 0) {
3533 http_log("Could not open output feed file '%s'\n",
3534 feed->feed_filename);
3535 exit(1);
3538 feed->feed_write_index = ffm_read_write_index(fd);
3539 feed->feed_size = lseek(fd, 0, SEEK_END);
3540 /* ensure that we do not wrap before the end of file */
3541 if (feed->feed_max_size && feed->feed_max_size < feed->feed_size)
3542 feed->feed_max_size = feed->feed_size;
3544 close(fd);
3548 /* compute the bandwidth used by each stream */
3549 static void compute_bandwidth(void)
3551 unsigned bandwidth;
3552 int i;
3553 FFStream *stream;
3555 for(stream = first_stream; stream != NULL; stream = stream->next) {
3556 bandwidth = 0;
3557 for(i=0;i<stream->nb_streams;i++) {
3558 AVStream *st = stream->streams[i];
3559 switch(st->codec->codec_type) {
3560 case CODEC_TYPE_AUDIO:
3561 case CODEC_TYPE_VIDEO:
3562 bandwidth += st->codec->bit_rate;
3563 break;
3564 default:
3565 break;
3568 stream->bandwidth = (bandwidth + 999) / 1000;
3572 static void get_arg(char *buf, int buf_size, const char **pp)
3574 const char *p;
3575 char *q;
3576 int quote;
3578 p = *pp;
3579 while (isspace(*p)) p++;
3580 q = buf;
3581 quote = 0;
3582 if (*p == '\"' || *p == '\'')
3583 quote = *p++;
3584 for(;;) {
3585 if (quote) {
3586 if (*p == quote)
3587 break;
3588 } else {
3589 if (isspace(*p))
3590 break;
3592 if (*p == '\0')
3593 break;
3594 if ((q - buf) < buf_size - 1)
3595 *q++ = *p;
3596 p++;
3598 *q = '\0';
3599 if (quote && *p == quote)
3600 p++;
3601 *pp = p;
3604 /* add a codec and set the default parameters */
3605 static void add_codec(FFStream *stream, AVCodecContext *av)
3607 AVStream *st;
3609 /* compute default parameters */
3610 switch(av->codec_type) {
3611 case CODEC_TYPE_AUDIO:
3612 if (av->bit_rate == 0)
3613 av->bit_rate = 64000;
3614 if (av->sample_rate == 0)
3615 av->sample_rate = 22050;
3616 if (av->channels == 0)
3617 av->channels = 1;
3618 break;
3619 case CODEC_TYPE_VIDEO:
3620 if (av->bit_rate == 0)
3621 av->bit_rate = 64000;
3622 if (av->time_base.num == 0){
3623 av->time_base.den = 5;
3624 av->time_base.num = 1;
3626 if (av->width == 0 || av->height == 0) {
3627 av->width = 160;
3628 av->height = 128;
3630 /* Bitrate tolerance is less for streaming */
3631 if (av->bit_rate_tolerance == 0)
3632 av->bit_rate_tolerance = FFMAX(av->bit_rate / 4,
3633 (int64_t)av->bit_rate*av->time_base.num/av->time_base.den);
3634 if (av->qmin == 0)
3635 av->qmin = 3;
3636 if (av->qmax == 0)
3637 av->qmax = 31;
3638 if (av->max_qdiff == 0)
3639 av->max_qdiff = 3;
3640 av->qcompress = 0.5;
3641 av->qblur = 0.5;
3643 if (!av->nsse_weight)
3644 av->nsse_weight = 8;
3646 av->frame_skip_cmp = FF_CMP_DCTMAX;
3647 av->me_method = ME_EPZS;
3648 av->rc_buffer_aggressivity = 1.0;
3650 if (!av->rc_eq)
3651 av->rc_eq = "tex^qComp";
3652 if (!av->i_quant_factor)
3653 av->i_quant_factor = -0.8;
3654 if (!av->b_quant_factor)
3655 av->b_quant_factor = 1.25;
3656 if (!av->b_quant_offset)
3657 av->b_quant_offset = 1.25;
3658 if (!av->rc_max_rate)
3659 av->rc_max_rate = av->bit_rate * 2;
3661 if (av->rc_max_rate && !av->rc_buffer_size) {
3662 av->rc_buffer_size = av->rc_max_rate;
3666 break;
3667 default:
3668 abort();
3671 st = av_mallocz(sizeof(AVStream));
3672 if (!st)
3673 return;
3674 st->codec = avcodec_alloc_context();
3675 stream->streams[stream->nb_streams++] = st;
3676 memcpy(st->codec, av, sizeof(AVCodecContext));
3679 static int opt_audio_codec(const char *arg)
3681 AVCodec *p= avcodec_find_encoder_by_name(arg);
3683 if (p == NULL || p->type != CODEC_TYPE_AUDIO)
3684 return CODEC_ID_NONE;
3686 return p->id;
3689 static int opt_video_codec(const char *arg)
3691 AVCodec *p= avcodec_find_encoder_by_name(arg);
3693 if (p == NULL || p->type != CODEC_TYPE_VIDEO)
3694 return CODEC_ID_NONE;
3696 return p->id;
3699 /* simplistic plugin support */
3701 #ifdef HAVE_DLOPEN
3702 static void load_module(const char *filename)
3704 void *dll;
3705 void (*init_func)(void);
3706 dll = dlopen(filename, RTLD_NOW);
3707 if (!dll) {
3708 fprintf(stderr, "Could not load module '%s' - %s\n",
3709 filename, dlerror());
3710 return;
3713 init_func = dlsym(dll, "ffserver_module_init");
3714 if (!init_func) {
3715 fprintf(stderr,
3716 "%s: init function 'ffserver_module_init()' not found\n",
3717 filename);
3718 dlclose(dll);
3721 init_func();
3723 #endif
3725 static int opt_default(const char *opt, const char *arg,
3726 AVCodecContext *avctx, int type)
3728 const AVOption *o = NULL;
3729 const AVOption *o2 = av_find_opt(avctx, opt, NULL, type, type);
3730 if(o2)
3731 o = av_set_string(avctx, opt, arg);
3732 if(!o)
3733 return -1;
3734 return 0;
3737 static int parse_ffconfig(const char *filename)
3739 FILE *f;
3740 char line[1024];
3741 char cmd[64];
3742 char arg[1024];
3743 const char *p;
3744 int val, errors, line_num;
3745 FFStream **last_stream, *stream, *redirect;
3746 FFStream **last_feed, *feed;
3747 AVCodecContext audio_enc, video_enc;
3748 int audio_id, video_id;
3750 f = fopen(filename, "r");
3751 if (!f) {
3752 perror(filename);
3753 return -1;
3756 errors = 0;
3757 line_num = 0;
3758 first_stream = NULL;
3759 last_stream = &first_stream;
3760 first_feed = NULL;
3761 last_feed = &first_feed;
3762 stream = NULL;
3763 feed = NULL;
3764 redirect = NULL;
3765 audio_id = CODEC_ID_NONE;
3766 video_id = CODEC_ID_NONE;
3767 for(;;) {
3768 if (fgets(line, sizeof(line), f) == NULL)
3769 break;
3770 line_num++;
3771 p = line;
3772 while (isspace(*p))
3773 p++;
3774 if (*p == '\0' || *p == '#')
3775 continue;
3777 get_arg(cmd, sizeof(cmd), &p);
3779 if (!strcasecmp(cmd, "Port")) {
3780 get_arg(arg, sizeof(arg), &p);
3781 val = atoi(arg);
3782 if (val < 1 || val > 65536) {
3783 fprintf(stderr, "%s:%d: Invalid port: %s\n",
3784 filename, line_num, arg);
3785 errors++;
3787 my_http_addr.sin_port = htons(val);
3788 } else if (!strcasecmp(cmd, "BindAddress")) {
3789 get_arg(arg, sizeof(arg), &p);
3790 if (resolve_host(&my_http_addr.sin_addr, arg) != 0) {
3791 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
3792 filename, line_num, arg);
3793 errors++;
3795 } else if (!strcasecmp(cmd, "NoDaemon")) {
3796 ffserver_daemon = 0;
3797 } else if (!strcasecmp(cmd, "RTSPPort")) {
3798 get_arg(arg, sizeof(arg), &p);
3799 val = atoi(arg);
3800 if (val < 1 || val > 65536) {
3801 fprintf(stderr, "%s:%d: Invalid port: %s\n",
3802 filename, line_num, arg);
3803 errors++;
3805 my_rtsp_addr.sin_port = htons(atoi(arg));
3806 } else if (!strcasecmp(cmd, "RTSPBindAddress")) {
3807 get_arg(arg, sizeof(arg), &p);
3808 if (resolve_host(&my_rtsp_addr.sin_addr, arg) != 0) {
3809 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
3810 filename, line_num, arg);
3811 errors++;
3813 } else if (!strcasecmp(cmd, "MaxClients")) {
3814 get_arg(arg, sizeof(arg), &p);
3815 val = atoi(arg);
3816 if (val < 1 || val > HTTP_MAX_CONNECTIONS) {
3817 fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n",
3818 filename, line_num, arg);
3819 errors++;
3820 } else {
3821 nb_max_connections = val;
3823 } else if (!strcasecmp(cmd, "MaxBandwidth")) {
3824 int64_t llval;
3825 get_arg(arg, sizeof(arg), &p);
3826 llval = atoll(arg);
3827 if (llval < 10 || llval > 10000000) {
3828 fprintf(stderr, "%s:%d: Invalid MaxBandwidth: %s\n",
3829 filename, line_num, arg);
3830 errors++;
3831 } else
3832 max_bandwidth = llval;
3833 } else if (!strcasecmp(cmd, "CustomLog")) {
3834 if (!ffserver_debug)
3835 get_arg(logfilename, sizeof(logfilename), &p);
3836 } else if (!strcasecmp(cmd, "<Feed")) {
3837 /*********************************************/
3838 /* Feed related options */
3839 char *q;
3840 if (stream || feed) {
3841 fprintf(stderr, "%s:%d: Already in a tag\n",
3842 filename, line_num);
3843 } else {
3844 feed = av_mallocz(sizeof(FFStream));
3845 /* add in stream list */
3846 *last_stream = feed;
3847 last_stream = &feed->next;
3848 /* add in feed list */
3849 *last_feed = feed;
3850 last_feed = &feed->next_feed;
3852 get_arg(feed->filename, sizeof(feed->filename), &p);
3853 q = strrchr(feed->filename, '>');
3854 if (*q)
3855 *q = '\0';
3856 feed->fmt = guess_format("ffm", NULL, NULL);
3857 /* defaut feed file */
3858 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
3859 "/tmp/%s.ffm", feed->filename);
3860 feed->feed_max_size = 5 * 1024 * 1024;
3861 feed->is_feed = 1;
3862 feed->feed = feed; /* self feeding :-) */
3864 } else if (!strcasecmp(cmd, "Launch")) {
3865 if (feed) {
3866 int i;
3868 feed->child_argv = av_mallocz(64 * sizeof(char *));
3870 for (i = 0; i < 62; i++) {
3871 get_arg(arg, sizeof(arg), &p);
3872 if (!arg[0])
3873 break;
3875 feed->child_argv[i] = av_strdup(arg);
3878 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
3880 snprintf(feed->child_argv[i], 30+strlen(feed->filename),
3881 "http://%s:%d/%s",
3882 (my_http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" :
3883 inet_ntoa(my_http_addr.sin_addr),
3884 ntohs(my_http_addr.sin_port), feed->filename);
3886 if (ffserver_debug)
3888 int j;
3889 fprintf(stdout, "Launch commandline: ");
3890 for (j = 0; j <= i; j++)
3891 fprintf(stdout, "%s ", feed->child_argv[j]);
3892 fprintf(stdout, "\n");
3895 } else if (!strcasecmp(cmd, "ReadOnlyFile")) {
3896 if (feed) {
3897 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3898 feed->readonly = 1;
3899 } else if (stream) {
3900 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3902 } else if (!strcasecmp(cmd, "File")) {
3903 if (feed) {
3904 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3905 } else if (stream)
3906 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3907 } else if (!strcasecmp(cmd, "FileMaxSize")) {
3908 if (feed) {
3909 char *p1;
3910 double fsize;
3912 get_arg(arg, sizeof(arg), &p);
3913 p1 = arg;
3914 fsize = strtod(p1, &p1);
3915 switch(toupper(*p1)) {
3916 case 'K':
3917 fsize *= 1024;
3918 break;
3919 case 'M':
3920 fsize *= 1024 * 1024;
3921 break;
3922 case 'G':
3923 fsize *= 1024 * 1024 * 1024;
3924 break;
3926 feed->feed_max_size = (int64_t)fsize;
3928 } else if (!strcasecmp(cmd, "</Feed>")) {
3929 if (!feed) {
3930 fprintf(stderr, "%s:%d: No corresponding <Feed> for </Feed>\n",
3931 filename, line_num);
3932 errors++;
3934 feed = NULL;
3935 } else if (!strcasecmp(cmd, "<Stream")) {
3936 /*********************************************/
3937 /* Stream related options */
3938 char *q;
3939 if (stream || feed) {
3940 fprintf(stderr, "%s:%d: Already in a tag\n",
3941 filename, line_num);
3942 } else {
3943 const AVClass *class;
3944 stream = av_mallocz(sizeof(FFStream));
3945 *last_stream = stream;
3946 last_stream = &stream->next;
3948 get_arg(stream->filename, sizeof(stream->filename), &p);
3949 q = strrchr(stream->filename, '>');
3950 if (*q)
3951 *q = '\0';
3952 stream->fmt = guess_stream_format(NULL, stream->filename, NULL);
3953 /* fetch avclass so AVOption works
3954 * FIXME try to use avcodec_get_context_defaults2
3955 * without changing defaults too much */
3956 avcodec_get_context_defaults(&video_enc);
3957 class = video_enc.av_class;
3958 memset(&audio_enc, 0, sizeof(AVCodecContext));
3959 memset(&video_enc, 0, sizeof(AVCodecContext));
3960 audio_enc.av_class = class;
3961 video_enc.av_class = class;
3962 audio_id = CODEC_ID_NONE;
3963 video_id = CODEC_ID_NONE;
3964 if (stream->fmt) {
3965 audio_id = stream->fmt->audio_codec;
3966 video_id = stream->fmt->video_codec;
3969 } else if (!strcasecmp(cmd, "Feed")) {
3970 get_arg(arg, sizeof(arg), &p);
3971 if (stream) {
3972 FFStream *sfeed;
3974 sfeed = first_feed;
3975 while (sfeed != NULL) {
3976 if (!strcmp(sfeed->filename, arg))
3977 break;
3978 sfeed = sfeed->next_feed;
3980 if (!sfeed)
3981 fprintf(stderr, "%s:%d: feed '%s' not defined\n",
3982 filename, line_num, arg);
3983 else
3984 stream->feed = sfeed;
3986 } else if (!strcasecmp(cmd, "Format")) {
3987 get_arg(arg, sizeof(arg), &p);
3988 if (stream) {
3989 if (!strcmp(arg, "status")) {
3990 stream->stream_type = STREAM_TYPE_STATUS;
3991 stream->fmt = NULL;
3992 } else {
3993 stream->stream_type = STREAM_TYPE_LIVE;
3994 /* jpeg cannot be used here, so use single frame jpeg */
3995 if (!strcmp(arg, "jpeg"))
3996 strcpy(arg, "mjpeg");
3997 stream->fmt = guess_stream_format(arg, NULL, NULL);
3998 if (!stream->fmt) {
3999 fprintf(stderr, "%s:%d: Unknown Format: %s\n",
4000 filename, line_num, arg);
4001 errors++;
4004 if (stream->fmt) {
4005 audio_id = stream->fmt->audio_codec;
4006 video_id = stream->fmt->video_codec;
4009 } else if (!strcasecmp(cmd, "InputFormat")) {
4010 get_arg(arg, sizeof(arg), &p);
4011 stream->ifmt = av_find_input_format(arg);
4012 if (!stream->ifmt) {
4013 fprintf(stderr, "%s:%d: Unknown input format: %s\n",
4014 filename, line_num, arg);
4016 } else if (!strcasecmp(cmd, "FaviconURL")) {
4017 if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
4018 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4019 } else {
4020 fprintf(stderr, "%s:%d: FaviconURL only permitted for status streams\n",
4021 filename, line_num);
4022 errors++;
4024 } else if (!strcasecmp(cmd, "Author")) {
4025 if (stream)
4026 get_arg(stream->author, sizeof(stream->author), &p);
4027 } else if (!strcasecmp(cmd, "Comment")) {
4028 if (stream)
4029 get_arg(stream->comment, sizeof(stream->comment), &p);
4030 } else if (!strcasecmp(cmd, "Copyright")) {
4031 if (stream)
4032 get_arg(stream->copyright, sizeof(stream->copyright), &p);
4033 } else if (!strcasecmp(cmd, "Title")) {
4034 if (stream)
4035 get_arg(stream->title, sizeof(stream->title), &p);
4036 } else if (!strcasecmp(cmd, "Preroll")) {
4037 get_arg(arg, sizeof(arg), &p);
4038 if (stream)
4039 stream->prebuffer = atof(arg) * 1000;
4040 } else if (!strcasecmp(cmd, "StartSendOnKey")) {
4041 if (stream)
4042 stream->send_on_key = 1;
4043 } else if (!strcasecmp(cmd, "AudioCodec")) {
4044 get_arg(arg, sizeof(arg), &p);
4045 audio_id = opt_audio_codec(arg);
4046 if (audio_id == CODEC_ID_NONE) {
4047 fprintf(stderr, "%s:%d: Unknown AudioCodec: %s\n",
4048 filename, line_num, arg);
4049 errors++;
4051 } else if (!strcasecmp(cmd, "VideoCodec")) {
4052 get_arg(arg, sizeof(arg), &p);
4053 video_id = opt_video_codec(arg);
4054 if (video_id == CODEC_ID_NONE) {
4055 fprintf(stderr, "%s:%d: Unknown VideoCodec: %s\n",
4056 filename, line_num, arg);
4057 errors++;
4059 } else if (!strcasecmp(cmd, "MaxTime")) {
4060 get_arg(arg, sizeof(arg), &p);
4061 if (stream)
4062 stream->max_time = atof(arg) * 1000;
4063 } else if (!strcasecmp(cmd, "AudioBitRate")) {
4064 get_arg(arg, sizeof(arg), &p);
4065 if (stream)
4066 audio_enc.bit_rate = atoi(arg) * 1000;
4067 } else if (!strcasecmp(cmd, "AudioChannels")) {
4068 get_arg(arg, sizeof(arg), &p);
4069 if (stream)
4070 audio_enc.channels = atoi(arg);
4071 } else if (!strcasecmp(cmd, "AudioSampleRate")) {
4072 get_arg(arg, sizeof(arg), &p);
4073 if (stream)
4074 audio_enc.sample_rate = atoi(arg);
4075 } else if (!strcasecmp(cmd, "AudioQuality")) {
4076 get_arg(arg, sizeof(arg), &p);
4077 if (stream) {
4078 // audio_enc.quality = atof(arg) * 1000;
4080 } else if (!strcasecmp(cmd, "VideoBitRateRange")) {
4081 if (stream) {
4082 int minrate, maxrate;
4084 get_arg(arg, sizeof(arg), &p);
4086 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
4087 video_enc.rc_min_rate = minrate * 1000;
4088 video_enc.rc_max_rate = maxrate * 1000;
4089 } else {
4090 fprintf(stderr, "%s:%d: Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n",
4091 filename, line_num, arg);
4092 errors++;
4095 } else if (!strcasecmp(cmd, "Debug")) {
4096 if (stream) {
4097 get_arg(arg, sizeof(arg), &p);
4098 video_enc.debug = strtol(arg,0,0);
4100 } else if (!strcasecmp(cmd, "Strict")) {
4101 if (stream) {
4102 get_arg(arg, sizeof(arg), &p);
4103 video_enc.strict_std_compliance = atoi(arg);
4105 } else if (!strcasecmp(cmd, "VideoBufferSize")) {
4106 if (stream) {
4107 get_arg(arg, sizeof(arg), &p);
4108 video_enc.rc_buffer_size = atoi(arg) * 8*1024;
4110 } else if (!strcasecmp(cmd, "VideoBitRateTolerance")) {
4111 if (stream) {
4112 get_arg(arg, sizeof(arg), &p);
4113 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
4115 } else if (!strcasecmp(cmd, "VideoBitRate")) {
4116 get_arg(arg, sizeof(arg), &p);
4117 if (stream) {
4118 video_enc.bit_rate = atoi(arg) * 1000;
4120 } else if (!strcasecmp(cmd, "VideoSize")) {
4121 get_arg(arg, sizeof(arg), &p);
4122 if (stream) {
4123 av_parse_video_frame_size(&video_enc.width, &video_enc.height, arg);
4124 if ((video_enc.width % 16) != 0 ||
4125 (video_enc.height % 16) != 0) {
4126 fprintf(stderr, "%s:%d: Image size must be a multiple of 16\n",
4127 filename, line_num);
4128 errors++;
4131 } else if (!strcasecmp(cmd, "VideoFrameRate")) {
4132 get_arg(arg, sizeof(arg), &p);
4133 if (stream) {
4134 AVRational frame_rate;
4135 if (av_parse_video_frame_rate(&frame_rate, arg) < 0) {
4136 fprintf(stderr, "Incorrect frame rate\n");
4137 errors++;
4138 } else {
4139 video_enc.time_base.num = frame_rate.den;
4140 video_enc.time_base.den = frame_rate.num;
4143 } else if (!strcasecmp(cmd, "VideoGopSize")) {
4144 get_arg(arg, sizeof(arg), &p);
4145 if (stream)
4146 video_enc.gop_size = atoi(arg);
4147 } else if (!strcasecmp(cmd, "VideoIntraOnly")) {
4148 if (stream)
4149 video_enc.gop_size = 1;
4150 } else if (!strcasecmp(cmd, "VideoHighQuality")) {
4151 if (stream)
4152 video_enc.mb_decision = FF_MB_DECISION_BITS;
4153 } else if (!strcasecmp(cmd, "Video4MotionVector")) {
4154 if (stream) {
4155 video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove
4156 video_enc.flags |= CODEC_FLAG_4MV;
4158 } else if (!strcasecmp(cmd, "AVOptionVideo") ||
4159 !strcasecmp(cmd, "AVOptionAudio")) {
4160 char arg2[1024];
4161 AVCodecContext *avctx;
4162 int type;
4163 get_arg(arg, sizeof(arg), &p);
4164 get_arg(arg2, sizeof(arg2), &p);
4165 if (!strcasecmp(cmd, "AVOptionVideo")) {
4166 avctx = &video_enc;
4167 type = AV_OPT_FLAG_VIDEO_PARAM;
4168 } else {
4169 avctx = &audio_enc;
4170 type = AV_OPT_FLAG_AUDIO_PARAM;
4172 if (opt_default(arg, arg2, avctx, type|AV_OPT_FLAG_ENCODING_PARAM)) {
4173 fprintf(stderr, "AVOption error: %s %s\n", arg, arg2);
4174 errors++;
4176 } else if (!strcasecmp(cmd, "VideoTag")) {
4177 get_arg(arg, sizeof(arg), &p);
4178 if ((strlen(arg) == 4) && stream)
4179 video_enc.codec_tag = ff_get_fourcc(arg);
4180 } else if (!strcasecmp(cmd, "BitExact")) {
4181 if (stream)
4182 video_enc.flags |= CODEC_FLAG_BITEXACT;
4183 } else if (!strcasecmp(cmd, "DctFastint")) {
4184 if (stream)
4185 video_enc.dct_algo = FF_DCT_FASTINT;
4186 } else if (!strcasecmp(cmd, "IdctSimple")) {
4187 if (stream)
4188 video_enc.idct_algo = FF_IDCT_SIMPLE;
4189 } else if (!strcasecmp(cmd, "Qscale")) {
4190 get_arg(arg, sizeof(arg), &p);
4191 if (stream) {
4192 video_enc.flags |= CODEC_FLAG_QSCALE;
4193 video_enc.global_quality = FF_QP2LAMBDA * atoi(arg);
4195 } else if (!strcasecmp(cmd, "VideoQDiff")) {
4196 get_arg(arg, sizeof(arg), &p);
4197 if (stream) {
4198 video_enc.max_qdiff = atoi(arg);
4199 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
4200 fprintf(stderr, "%s:%d: VideoQDiff out of range\n",
4201 filename, line_num);
4202 errors++;
4205 } else if (!strcasecmp(cmd, "VideoQMax")) {
4206 get_arg(arg, sizeof(arg), &p);
4207 if (stream) {
4208 video_enc.qmax = atoi(arg);
4209 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
4210 fprintf(stderr, "%s:%d: VideoQMax out of range\n",
4211 filename, line_num);
4212 errors++;
4215 } else if (!strcasecmp(cmd, "VideoQMin")) {
4216 get_arg(arg, sizeof(arg), &p);
4217 if (stream) {
4218 video_enc.qmin = atoi(arg);
4219 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
4220 fprintf(stderr, "%s:%d: VideoQMin out of range\n",
4221 filename, line_num);
4222 errors++;
4225 } else if (!strcasecmp(cmd, "LumaElim")) {
4226 get_arg(arg, sizeof(arg), &p);
4227 if (stream)
4228 video_enc.luma_elim_threshold = atoi(arg);
4229 } else if (!strcasecmp(cmd, "ChromaElim")) {
4230 get_arg(arg, sizeof(arg), &p);
4231 if (stream)
4232 video_enc.chroma_elim_threshold = atoi(arg);
4233 } else if (!strcasecmp(cmd, "LumiMask")) {
4234 get_arg(arg, sizeof(arg), &p);
4235 if (stream)
4236 video_enc.lumi_masking = atof(arg);
4237 } else if (!strcasecmp(cmd, "DarkMask")) {
4238 get_arg(arg, sizeof(arg), &p);
4239 if (stream)
4240 video_enc.dark_masking = atof(arg);
4241 } else if (!strcasecmp(cmd, "NoVideo")) {
4242 video_id = CODEC_ID_NONE;
4243 } else if (!strcasecmp(cmd, "NoAudio")) {
4244 audio_id = CODEC_ID_NONE;
4245 } else if (!strcasecmp(cmd, "ACL")) {
4246 IPAddressACL acl;
4248 get_arg(arg, sizeof(arg), &p);
4249 if (strcasecmp(arg, "allow") == 0)
4250 acl.action = IP_ALLOW;
4251 else if (strcasecmp(arg, "deny") == 0)
4252 acl.action = IP_DENY;
4253 else {
4254 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
4255 filename, line_num, arg);
4256 errors++;
4259 get_arg(arg, sizeof(arg), &p);
4261 if (resolve_host(&acl.first, arg) != 0) {
4262 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4263 filename, line_num, arg);
4264 errors++;
4265 } else
4266 acl.last = acl.first;
4268 get_arg(arg, sizeof(arg), &p);
4270 if (arg[0]) {
4271 if (resolve_host(&acl.last, arg) != 0) {
4272 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4273 filename, line_num, arg);
4274 errors++;
4278 if (!errors) {
4279 IPAddressACL *nacl = av_mallocz(sizeof(*nacl));
4280 IPAddressACL **naclp = 0;
4282 acl.next = 0;
4283 *nacl = acl;
4285 if (stream)
4286 naclp = &stream->acl;
4287 else if (feed)
4288 naclp = &feed->acl;
4289 else {
4290 fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
4291 filename, line_num);
4292 errors++;
4295 if (naclp) {
4296 while (*naclp)
4297 naclp = &(*naclp)->next;
4299 *naclp = nacl;
4302 } else if (!strcasecmp(cmd, "RTSPOption")) {
4303 get_arg(arg, sizeof(arg), &p);
4304 if (stream) {
4305 av_freep(&stream->rtsp_option);
4306 stream->rtsp_option = av_strdup(arg);
4308 } else if (!strcasecmp(cmd, "MulticastAddress")) {
4309 get_arg(arg, sizeof(arg), &p);
4310 if (stream) {
4311 if (resolve_host(&stream->multicast_ip, arg) != 0) {
4312 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
4313 filename, line_num, arg);
4314 errors++;
4316 stream->is_multicast = 1;
4317 stream->loop = 1; /* default is looping */
4319 } else if (!strcasecmp(cmd, "MulticastPort")) {
4320 get_arg(arg, sizeof(arg), &p);
4321 if (stream)
4322 stream->multicast_port = atoi(arg);
4323 } else if (!strcasecmp(cmd, "MulticastTTL")) {
4324 get_arg(arg, sizeof(arg), &p);
4325 if (stream)
4326 stream->multicast_ttl = atoi(arg);
4327 } else if (!strcasecmp(cmd, "NoLoop")) {
4328 if (stream)
4329 stream->loop = 0;
4330 } else if (!strcasecmp(cmd, "</Stream>")) {
4331 if (!stream) {
4332 fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n",
4333 filename, line_num);
4334 errors++;
4335 } else {
4336 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
4337 if (audio_id != CODEC_ID_NONE) {
4338 audio_enc.codec_type = CODEC_TYPE_AUDIO;
4339 audio_enc.codec_id = audio_id;
4340 add_codec(stream, &audio_enc);
4342 if (video_id != CODEC_ID_NONE) {
4343 video_enc.codec_type = CODEC_TYPE_VIDEO;
4344 video_enc.codec_id = video_id;
4345 add_codec(stream, &video_enc);
4348 stream = NULL;
4350 } else if (!strcasecmp(cmd, "<Redirect")) {
4351 /*********************************************/
4352 char *q;
4353 if (stream || feed || redirect) {
4354 fprintf(stderr, "%s:%d: Already in a tag\n",
4355 filename, line_num);
4356 errors++;
4357 } else {
4358 redirect = av_mallocz(sizeof(FFStream));
4359 *last_stream = redirect;
4360 last_stream = &redirect->next;
4362 get_arg(redirect->filename, sizeof(redirect->filename), &p);
4363 q = strrchr(redirect->filename, '>');
4364 if (*q)
4365 *q = '\0';
4366 redirect->stream_type = STREAM_TYPE_REDIRECT;
4368 } else if (!strcasecmp(cmd, "URL")) {
4369 if (redirect)
4370 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4371 } else if (!strcasecmp(cmd, "</Redirect>")) {
4372 if (!redirect) {
4373 fprintf(stderr, "%s:%d: No corresponding <Redirect> for </Redirect>\n",
4374 filename, line_num);
4375 errors++;
4376 } else {
4377 if (!redirect->feed_filename[0]) {
4378 fprintf(stderr, "%s:%d: No URL found for <Redirect>\n",
4379 filename, line_num);
4380 errors++;
4382 redirect = NULL;
4384 } else if (!strcasecmp(cmd, "LoadModule")) {
4385 get_arg(arg, sizeof(arg), &p);
4386 #ifdef HAVE_DLOPEN
4387 load_module(arg);
4388 #else
4389 fprintf(stderr, "%s:%d: Module support not compiled into this version: '%s'\n",
4390 filename, line_num, arg);
4391 errors++;
4392 #endif
4393 } else {
4394 fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n",
4395 filename, line_num, cmd);
4396 errors++;
4400 fclose(f);
4401 if (errors)
4402 return -1;
4403 else
4404 return 0;
4407 static void handle_child_exit(int sig)
4409 pid_t pid;
4410 int status;
4412 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
4413 FFStream *feed;
4415 for (feed = first_feed; feed; feed = feed->next) {
4416 if (feed->pid == pid) {
4417 int uptime = time(0) - feed->pid_start;
4419 feed->pid = 0;
4420 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
4422 if (uptime < 30)
4423 /* Turn off any more restarts */
4424 feed->child_argv = 0;
4429 need_to_start_children = 1;
4432 static void opt_debug()
4434 ffserver_debug = 1;
4435 ffserver_daemon = 0;
4436 logfilename[0] = '-';
4439 static void opt_show_help(void)
4441 printf("usage: ffserver [options]\n"
4442 "Hyper fast multi format Audio/Video streaming server\n");
4443 printf("\n");
4444 show_help_options(options, "Main options:\n", 0, 0);
4447 static const OptionDef options[] = {
4448 { "h", OPT_EXIT, {(void*)opt_show_help}, "show help" },
4449 { "version", OPT_EXIT, {(void*)show_version}, "show version" },
4450 { "L", OPT_EXIT, {(void*)show_license}, "show license" },
4451 { "formats", OPT_EXIT, {(void*)show_formats}, "show available formats, codecs, protocols, ..." },
4452 { "n", OPT_BOOL, {(void *)&no_launch }, "enable no-launch mode" },
4453 { "d", 0, {(void*)opt_debug}, "enable debug mode" },
4454 { "f", HAS_ARG | OPT_STRING, {(void*)&config_filename }, "use configfile instead of /etc/ffserver.conf", "configfile" },
4455 { NULL },
4458 int main(int argc, char **argv)
4460 struct sigaction sigact;
4462 av_register_all();
4464 show_banner();
4466 config_filename = "/etc/ffserver.conf";
4468 my_program_name = argv[0];
4469 my_program_dir = getcwd(0, 0);
4470 ffserver_daemon = 1;
4472 parse_options(argc, argv, options, NULL);
4474 unsetenv("http_proxy"); /* Kill the http_proxy */
4476 av_init_random(av_gettime() + (getpid() << 16), &random_state);
4478 memset(&sigact, 0, sizeof(sigact));
4479 sigact.sa_handler = handle_child_exit;
4480 sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
4481 sigaction(SIGCHLD, &sigact, 0);
4483 if (parse_ffconfig(config_filename) < 0) {
4484 fprintf(stderr, "Incorrect config file - exiting.\n");
4485 exit(1);
4488 build_file_streams();
4490 build_feed_streams();
4492 compute_bandwidth();
4494 /* put the process in background and detach it from its TTY */
4495 if (ffserver_daemon) {
4496 int pid;
4498 pid = fork();
4499 if (pid < 0) {
4500 perror("fork");
4501 exit(1);
4502 } else if (pid > 0) {
4503 /* parent : exit */
4504 exit(0);
4505 } else {
4506 /* child */
4507 setsid();
4508 close(0);
4509 open("/dev/null", O_RDWR);
4510 if (strcmp(logfilename, "-") != 0) {
4511 close(1);
4512 dup(0);
4514 close(2);
4515 dup(0);
4519 /* signal init */
4520 signal(SIGPIPE, SIG_IGN);
4522 /* open log file if needed */
4523 if (logfilename[0] != '\0') {
4524 if (!strcmp(logfilename, "-"))
4525 logfile = stderr;
4526 else
4527 logfile = fopen(logfilename, "a");
4528 av_log_set_callback(http_av_log);
4531 if (ffserver_daemon)
4532 chdir("/");
4534 if (http_server() < 0) {
4535 http_log("Could not start server\n");
4536 exit(1);
4539 return 0;