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
23 #ifndef HAVE_CLOSESOCKET
24 #define closesocket close
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"
39 #include <sys/ioctl.h>
45 #undef time //needed because HAVE_AV_CONFIG_H is defined on top
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
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 */
75 RTSPSTATE_WAIT_REQUEST
,
77 RTSPSTATE_SEND_PACKET
,
80 static const char *http_state
[] = {
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)
105 int64_t count1
, count2
;
106 int64_t time1
, time2
;
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 */
116 uint8_t *buffer_ptr
, *buffer_end
;
119 struct HTTPContext
*next
;
120 int got_key_frame
; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
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
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 */
141 AVFormatContext fmt_ctx
; /* instance of FFStream for one user */
142 int last_packet_sent
; /* true if last data packet was sent */
144 DataRateData datarate
;
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 */
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
;
172 /* each generated stream is described here */
176 STREAM_TYPE_REDIRECT
,
179 enum IPAddressAction
{
184 typedef struct IPAddressACL
{
185 struct IPAddressACL
*next
;
186 enum IPAddressAction action
;
187 /* These are in host order */
188 struct in_addr first
;
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
198 AVFormatParameters
*ap_in
; /* input parameters */
199 AVInputFormat
*ifmt
; /* if non NULL, force input format */
203 int prebuffer
; /* Number of millseconds early to start */
204 int64_t max_time
; /* Number of milliseconds to run */
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 */
214 pid_t pid
; /* Of ffmpeg process */
215 time_t pid_start
; /* Of ffmpeg process */
217 struct FFStream
*next
;
218 unsigned bandwidth
; /* bandwidth, in kbits/s */
221 /* multicast specific */
223 struct in_addr multicast_ip
;
224 int multicast_port
; /* first port used for multicast */
226 int loop
; /* if true, send the stream in loops (only meaningful if file) */
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 */
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
;
240 typedef struct FeedData
{
241 long long data_count
;
242 float avg_frame_size
; /* frame size averaged over last frames with exponential mean */
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
);
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
);
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
);
275 static int prepare_sdp_description(FFStream
*stream
, uint8_t **pbuffer
,
276 struct in_addr my_ip
);
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
)
315 p
= buf2
+ strlen(p
) - 1;
321 static void http_vlog(const char *fmt
, va_list vargs
)
323 static int print_prefix
= 1;
328 fprintf(logfile
, "%s ", buf
);
330 print_prefix
= strstr(fmt
, "\n") != NULL
;
331 vfprintf(logfile
, fmt
, vargs
);
336 void __attribute__ ((format (printf
, 1, 2))) http_log(const char *fmt
, ...)
339 va_start(vargs
, fmt
);
340 http_vlog(fmt
, 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
)
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
)
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
;
379 /* In bytes per second */
380 static int compute_datarate(DataRateData
*drd
, int64_t count
)
382 if (cur_time
== drd
->time1
)
385 return ((count
- drd
->count1
) * 1000) / (cur_time
- drd
->time1
);
389 static void start_children(FFStream
*feed
)
394 for (; feed
; feed
= feed
->next
) {
395 if (feed
->child_argv
&& !feed
->pid
) {
396 feed
->pid_start
= time(0);
401 http_log("Unable to create children\n");
410 av_strlcpy(pathname
, my_program_name
, sizeof(pathname
));
412 slash
= strrchr(pathname
, '/');
417 strcpy(slash
, "ffmpeg");
419 http_log("Launch commandline: ");
420 http_log("%s ", pathname
);
421 for (i
= 1; feed
->child_argv
[i
] && feed
->child_argv
[i
][0]; i
++)
422 http_log("%s ", feed
->child_argv
[i
]);
425 for (i
= 3; i
< 256; i
++)
428 if (!ffserver_debug
) {
429 i
= open("/dev/null", O_RDWR
);
438 /* This is needed to make relative pathnames work */
439 chdir(my_program_dir
);
441 signal(SIGPIPE
, SIG_DFL
);
443 execvp(pathname
, feed
->child_argv
);
451 /* open a listening socket */
452 static int socket_open_listen(struct sockaddr_in
*my_addr
)
456 server_fd
= socket(AF_INET
,SOCK_STREAM
,0);
463 setsockopt(server_fd
, SOL_SOCKET
, SO_REUSEADDR
, &tmp
, sizeof(tmp
));
465 if (bind (server_fd
, (struct sockaddr
*) my_addr
, sizeof (*my_addr
)) < 0) {
467 snprintf(bindmsg
, sizeof(bindmsg
), "bind(port %d)", ntohs(my_addr
->sin_port
));
469 closesocket(server_fd
);
473 if (listen (server_fd
, 5) < 0) {
475 closesocket(server_fd
);
478 ff_socket_nonblock(server_fd
, 1);
483 /* start all multicast streams */
484 static void start_multicast(void)
489 struct sockaddr_in dest_addr
;
490 int default_port
, stream_index
;
493 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
494 if (stream
->is_multicast
) {
495 /* open the RTP connection */
496 snprintf(session_id
, sizeof(session_id
), "%08x%08x",
497 av_random(&random_state
), av_random(&random_state
));
499 /* choose a port if none given */
500 if (stream
->multicast_port
== 0) {
501 stream
->multicast_port
= default_port
;
505 dest_addr
.sin_family
= AF_INET
;
506 dest_addr
.sin_addr
= stream
->multicast_ip
;
507 dest_addr
.sin_port
= htons(stream
->multicast_port
);
509 rtp_c
= rtp_new_connection(&dest_addr
, stream
, session_id
,
510 RTSP_PROTOCOL_RTP_UDP_MULTICAST
);
514 if (open_input_stream(rtp_c
, "") < 0) {
515 http_log("Could not open input stream for stream '%s'\n",
520 /* open each RTP stream */
521 for(stream_index
= 0; stream_index
< stream
->nb_streams
;
523 dest_addr
.sin_port
= htons(stream
->multicast_port
+
525 if (rtp_new_av_stream(rtp_c
, stream_index
, &dest_addr
, NULL
) < 0) {
526 http_log("Could not open output stream '%s/streamid=%d'\n",
527 stream
->filename
, stream_index
);
532 /* change state to send data */
533 rtp_c
->state
= HTTPSTATE_SEND_DATA
;
538 /* main loop of the http server */
539 static int http_server(void)
541 int server_fd
= 0, rtsp_server_fd
= 0;
542 int ret
, delay
, delay1
;
543 struct pollfd poll_table
[HTTP_MAX_CONNECTIONS
+ 2], *poll_entry
;
544 HTTPContext
*c
, *c_next
;
546 if (my_http_addr
.sin_port
) {
547 server_fd
= socket_open_listen(&my_http_addr
);
552 if (my_rtsp_addr
.sin_port
) {
553 rtsp_server_fd
= socket_open_listen(&my_rtsp_addr
);
554 if (rtsp_server_fd
< 0)
558 if (!rtsp_server_fd
&& !server_fd
) {
559 http_log("HTTP and RTSP disabled.\n");
563 http_log("ffserver started.\n");
565 start_children(first_feed
);
570 poll_entry
= poll_table
;
572 poll_entry
->fd
= server_fd
;
573 poll_entry
->events
= POLLIN
;
576 if (rtsp_server_fd
) {
577 poll_entry
->fd
= rtsp_server_fd
;
578 poll_entry
->events
= POLLIN
;
582 /* wait for events on each HTTP handle */
589 case HTTPSTATE_SEND_HEADER
:
590 case RTSPSTATE_SEND_REPLY
:
591 case RTSPSTATE_SEND_PACKET
:
592 c
->poll_entry
= poll_entry
;
594 poll_entry
->events
= POLLOUT
;
597 case HTTPSTATE_SEND_DATA_HEADER
:
598 case HTTPSTATE_SEND_DATA
:
599 case HTTPSTATE_SEND_DATA_TRAILER
:
600 if (!c
->is_packetized
) {
601 /* for TCP, we output as much as we can (may need to put a limit) */
602 c
->poll_entry
= poll_entry
;
604 poll_entry
->events
= POLLOUT
;
607 /* when ffserver is doing the timing, we work by
608 looking at which packet need to be sent every
610 delay1
= 10; /* one tick wait XXX: 10 ms assumed */
615 case HTTPSTATE_WAIT_REQUEST
:
616 case HTTPSTATE_RECEIVE_DATA
:
617 case HTTPSTATE_WAIT_FEED
:
618 case RTSPSTATE_WAIT_REQUEST
:
619 /* need to catch errors */
620 c
->poll_entry
= poll_entry
;
622 poll_entry
->events
= POLLIN
;/* Maybe this will work */
626 c
->poll_entry
= NULL
;
632 /* wait for an event on one connection. We poll at least every
633 second to handle timeouts */
635 ret
= poll(poll_table
, poll_entry
- poll_table
, delay
);
636 if (ret
< 0 && ff_neterrno() != FF_NETERROR(EAGAIN
) &&
637 ff_neterrno() != FF_NETERROR(EINTR
))
641 cur_time
= av_gettime() / 1000;
643 if (need_to_start_children
) {
644 need_to_start_children
= 0;
645 start_children(first_feed
);
648 /* now handle the events */
649 for(c
= first_http_ctx
; c
!= NULL
; c
= c_next
) {
651 if (handle_connection(c
) < 0) {
652 /* close and free the connection */
658 poll_entry
= poll_table
;
660 /* new HTTP connection request ? */
661 if (poll_entry
->revents
& POLLIN
)
662 new_connection(server_fd
, 0);
665 if (rtsp_server_fd
) {
666 /* new RTSP connection request ? */
667 if (poll_entry
->revents
& POLLIN
)
668 new_connection(rtsp_server_fd
, 1);
673 /* start waiting for a new HTTP/RTSP request */
674 static void start_wait_request(HTTPContext
*c
, int is_rtsp
)
676 c
->buffer_ptr
= c
->buffer
;
677 c
->buffer_end
= c
->buffer
+ c
->buffer_size
- 1; /* leave room for '\0' */
680 c
->timeout
= cur_time
+ RTSP_REQUEST_TIMEOUT
;
681 c
->state
= RTSPSTATE_WAIT_REQUEST
;
683 c
->timeout
= cur_time
+ HTTP_REQUEST_TIMEOUT
;
684 c
->state
= HTTPSTATE_WAIT_REQUEST
;
688 static void new_connection(int server_fd
, int is_rtsp
)
690 struct sockaddr_in from_addr
;
692 HTTPContext
*c
= NULL
;
694 len
= sizeof(from_addr
);
695 fd
= accept(server_fd
, (struct sockaddr
*)&from_addr
,
698 http_log("error during accept %s\n", strerror(errno
));
701 ff_socket_nonblock(fd
, 1);
703 /* XXX: should output a warning page when coming
704 close to the connection limit */
705 if (nb_connections
>= nb_max_connections
)
708 /* add a new connection */
709 c
= av_mallocz(sizeof(HTTPContext
));
714 c
->poll_entry
= NULL
;
715 c
->from_addr
= from_addr
;
716 c
->buffer_size
= IOBUFFER_INIT_SIZE
;
717 c
->buffer
= av_malloc(c
->buffer_size
);
721 c
->next
= first_http_ctx
;
725 start_wait_request(c
, is_rtsp
);
737 static void close_connection(HTTPContext
*c
)
739 HTTPContext
**cp
, *c1
;
741 AVFormatContext
*ctx
;
745 /* remove connection from list */
746 cp
= &first_http_ctx
;
747 while ((*cp
) != NULL
) {
755 /* remove references, if any (XXX: do it faster) */
756 for(c1
= first_http_ctx
; c1
!= NULL
; c1
= c1
->next
) {
761 /* remove connection associated resources */
765 /* close each frame parser */
766 for(i
=0;i
<c
->fmt_in
->nb_streams
;i
++) {
767 st
= c
->fmt_in
->streams
[i
];
768 if (st
->codec
->codec
)
769 avcodec_close(st
->codec
);
771 av_close_input_file(c
->fmt_in
);
774 /* free RTP output streams if any */
777 nb_streams
= c
->stream
->nb_streams
;
779 for(i
=0;i
<nb_streams
;i
++) {
782 av_write_trailer(ctx
);
785 h
= c
->rtp_handles
[i
];
792 if (!c
->last_packet_sent
) {
795 if (url_open_dyn_buf(&ctx
->pb
) >= 0) {
796 av_write_trailer(ctx
);
797 av_freep(&c
->pb_buffer
);
798 url_close_dyn_buf(ctx
->pb
, &c
->pb_buffer
);
803 for(i
=0; i
<ctx
->nb_streams
; i
++)
804 av_free(ctx
->streams
[i
]);
806 if (c
->stream
&& !c
->post
&& c
->stream
->stream_type
== STREAM_TYPE_LIVE
)
807 current_bandwidth
-= c
->stream
->bandwidth
;
809 /* signal that there is no feed if we are the feeder socket */
810 if (c
->state
== HTTPSTATE_RECEIVE_DATA
&& c
->stream
) {
811 c
->stream
->feed_opened
= 0;
815 av_freep(&c
->pb_buffer
);
816 av_freep(&c
->packet_buffer
);
822 static int handle_connection(HTTPContext
*c
)
827 case HTTPSTATE_WAIT_REQUEST
:
828 case RTSPSTATE_WAIT_REQUEST
:
830 if ((c
->timeout
- cur_time
) < 0)
832 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
))
835 /* no need to read if no events */
836 if (!(c
->poll_entry
->revents
& POLLIN
))
840 len
= recv(c
->fd
, c
->buffer_ptr
, 1, 0);
842 if (ff_neterrno() != FF_NETERROR(EAGAIN
) &&
843 ff_neterrno() != FF_NETERROR(EINTR
))
845 } else if (len
== 0) {
848 /* search for end of request. */
850 c
->buffer_ptr
+= len
;
852 if ((ptr
>= c
->buffer
+ 2 && !memcmp(ptr
-2, "\n\n", 2)) ||
853 (ptr
>= c
->buffer
+ 4 && !memcmp(ptr
-4, "\r\n\r\n", 4))) {
854 /* request found : parse it and reply */
855 if (c
->state
== HTTPSTATE_WAIT_REQUEST
) {
856 ret
= http_parse_request(c
);
858 ret
= rtsp_parse_request(c
);
862 } else if (ptr
>= c
->buffer_end
) {
863 /* request too long: cannot do anything */
865 } else goto read_loop
;
869 case HTTPSTATE_SEND_HEADER
:
870 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
))
873 /* no need to write if no events */
874 if (!(c
->poll_entry
->revents
& POLLOUT
))
876 len
= send(c
->fd
, c
->buffer_ptr
, c
->buffer_end
- c
->buffer_ptr
, 0);
878 if (ff_neterrno() != FF_NETERROR(EAGAIN
) &&
879 ff_neterrno() != FF_NETERROR(EINTR
)) {
880 /* error : close connection */
881 av_freep(&c
->pb_buffer
);
885 c
->buffer_ptr
+= len
;
887 c
->stream
->bytes_served
+= len
;
888 c
->data_count
+= len
;
889 if (c
->buffer_ptr
>= c
->buffer_end
) {
890 av_freep(&c
->pb_buffer
);
894 /* all the buffer was sent : synchronize to the incoming stream */
895 c
->state
= HTTPSTATE_SEND_DATA_HEADER
;
896 c
->buffer_ptr
= c
->buffer_end
= c
->buffer
;
901 case HTTPSTATE_SEND_DATA
:
902 case HTTPSTATE_SEND_DATA_HEADER
:
903 case HTTPSTATE_SEND_DATA_TRAILER
:
904 /* for packetized output, we consider we can always write (the
905 input streams sets the speed). It may be better to verify
906 that we do not rely too much on the kernel queues */
907 if (!c
->is_packetized
) {
908 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
))
911 /* no need to read if no events */
912 if (!(c
->poll_entry
->revents
& POLLOUT
))
915 if (http_send_data(c
) < 0)
917 /* close connection if trailer sent */
918 if (c
->state
== HTTPSTATE_SEND_DATA_TRAILER
)
921 case HTTPSTATE_RECEIVE_DATA
:
922 /* no need to read if no events */
923 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
))
925 if (!(c
->poll_entry
->revents
& POLLIN
))
927 if (http_receive_data(c
) < 0)
930 case HTTPSTATE_WAIT_FEED
:
931 /* no need to read if no events */
932 if (c
->poll_entry
->revents
& (POLLIN
| POLLERR
| POLLHUP
))
935 /* nothing to do, we'll be waken up by incoming feed packets */
938 case RTSPSTATE_SEND_REPLY
:
939 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
)) {
940 av_freep(&c
->pb_buffer
);
943 /* no need to write if no events */
944 if (!(c
->poll_entry
->revents
& POLLOUT
))
946 len
= send(c
->fd
, c
->buffer_ptr
, c
->buffer_end
- c
->buffer_ptr
, 0);
948 if (ff_neterrno() != FF_NETERROR(EAGAIN
) &&
949 ff_neterrno() != FF_NETERROR(EINTR
)) {
950 /* error : close connection */
951 av_freep(&c
->pb_buffer
);
955 c
->buffer_ptr
+= len
;
956 c
->data_count
+= len
;
957 if (c
->buffer_ptr
>= c
->buffer_end
) {
958 /* all the buffer was sent : wait for a new request */
959 av_freep(&c
->pb_buffer
);
960 start_wait_request(c
, 1);
964 case RTSPSTATE_SEND_PACKET
:
965 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
)) {
966 av_freep(&c
->packet_buffer
);
969 /* no need to write if no events */
970 if (!(c
->poll_entry
->revents
& POLLOUT
))
972 len
= send(c
->fd
, c
->packet_buffer_ptr
,
973 c
->packet_buffer_end
- c
->packet_buffer_ptr
, 0);
975 if (ff_neterrno() != FF_NETERROR(EAGAIN
) &&
976 ff_neterrno() != FF_NETERROR(EINTR
)) {
977 /* error : close connection */
978 av_freep(&c
->packet_buffer
);
982 c
->packet_buffer_ptr
+= len
;
983 if (c
->packet_buffer_ptr
>= c
->packet_buffer_end
) {
984 /* all the buffer was sent : wait for a new request */
985 av_freep(&c
->packet_buffer
);
986 c
->state
= RTSPSTATE_WAIT_REQUEST
;
990 case HTTPSTATE_READY
:
999 static int extract_rates(char *rates
, int ratelen
, const char *request
)
1003 for (p
= request
; *p
&& *p
!= '\r' && *p
!= '\n'; ) {
1004 if (strncasecmp(p
, "Pragma:", 7) == 0) {
1005 const char *q
= p
+ 7;
1007 while (*q
&& *q
!= '\n' && isspace(*q
))
1010 if (strncasecmp(q
, "stream-switch-entry=", 20) == 0) {
1016 memset(rates
, 0xff, ratelen
);
1019 while (*q
&& *q
!= '\n' && *q
!= ':')
1022 if (sscanf(q
, ":%d:%d", &stream_no
, &rate_no
) != 2)
1026 if (stream_no
< ratelen
&& stream_no
>= 0)
1027 rates
[stream_no
] = rate_no
;
1029 while (*q
&& *q
!= '\n' && !isspace(*q
))
1036 p
= strchr(p
, '\n');
1046 static int find_stream_in_feed(FFStream
*feed
, AVCodecContext
*codec
, int bit_rate
)
1049 int best_bitrate
= 100000000;
1052 for (i
= 0; i
< feed
->nb_streams
; i
++) {
1053 AVCodecContext
*feed_codec
= feed
->streams
[i
]->codec
;
1055 if (feed_codec
->codec_id
!= codec
->codec_id
||
1056 feed_codec
->sample_rate
!= codec
->sample_rate
||
1057 feed_codec
->width
!= codec
->width
||
1058 feed_codec
->height
!= codec
->height
)
1061 /* Potential stream */
1063 /* We want the fastest stream less than bit_rate, or the slowest
1064 * faster than bit_rate
1067 if (feed_codec
->bit_rate
<= bit_rate
) {
1068 if (best_bitrate
> bit_rate
|| feed_codec
->bit_rate
> best_bitrate
) {
1069 best_bitrate
= feed_codec
->bit_rate
;
1073 if (feed_codec
->bit_rate
< best_bitrate
) {
1074 best_bitrate
= feed_codec
->bit_rate
;
1083 static int modify_current_stream(HTTPContext
*c
, char *rates
)
1086 FFStream
*req
= c
->stream
;
1087 int action_required
= 0;
1089 /* Not much we can do for a feed */
1093 for (i
= 0; i
< req
->nb_streams
; i
++) {
1094 AVCodecContext
*codec
= req
->streams
[i
]->codec
;
1098 c
->switch_feed_streams
[i
] = req
->feed_streams
[i
];
1101 c
->switch_feed_streams
[i
] = find_stream_in_feed(req
->feed
, codec
, codec
->bit_rate
/ 2);
1104 /* Wants off or slow */
1105 c
->switch_feed_streams
[i
] = find_stream_in_feed(req
->feed
, codec
, codec
->bit_rate
/ 4);
1107 /* This doesn't work well when it turns off the only stream! */
1108 c
->switch_feed_streams
[i
] = -2;
1109 c
->feed_streams
[i
] = -2;
1114 if (c
->switch_feed_streams
[i
] >= 0 && c
->switch_feed_streams
[i
] != c
->feed_streams
[i
])
1115 action_required
= 1;
1118 return action_required
;
1122 static void do_switch_stream(HTTPContext
*c
, int i
)
1124 if (c
->switch_feed_streams
[i
] >= 0) {
1126 c
->feed_streams
[i
] = c
->switch_feed_streams
[i
];
1129 /* Now update the stream */
1131 c
->switch_feed_streams
[i
] = -1;
1134 /* XXX: factorize in utils.c ? */
1135 /* XXX: take care with different space meaning */
1136 static void skip_spaces(const char **pp
)
1140 while (*p
== ' ' || *p
== '\t')
1145 static void get_word(char *buf
, int buf_size
, const char **pp
)
1153 while (!isspace(*p
) && *p
!= '\0') {
1154 if ((q
- buf
) < buf_size
- 1)
1163 static int validate_acl(FFStream
*stream
, HTTPContext
*c
)
1165 enum IPAddressAction last_action
= IP_DENY
;
1167 struct in_addr
*src
= &c
->from_addr
.sin_addr
;
1168 unsigned long src_addr
= src
->s_addr
;
1170 for (acl
= stream
->acl
; acl
; acl
= acl
->next
) {
1171 if (src_addr
>= acl
->first
.s_addr
&& src_addr
<= acl
->last
.s_addr
)
1172 return (acl
->action
== IP_ALLOW
) ? 1 : 0;
1173 last_action
= acl
->action
;
1176 /* Nothing matched, so return not the last action */
1177 return (last_action
== IP_DENY
) ? 1 : 0;
1180 /* compute the real filename of a file by matching it without its
1181 extensions to all the stream filenames */
1182 static void compute_real_filename(char *filename
, int max_size
)
1189 /* compute filename by matching without the file extensions */
1190 av_strlcpy(file1
, filename
, sizeof(file1
));
1191 p
= strrchr(file1
, '.');
1194 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
1195 av_strlcpy(file2
, stream
->filename
, sizeof(file2
));
1196 p
= strrchr(file2
, '.');
1199 if (!strcmp(file1
, file2
)) {
1200 av_strlcpy(filename
, stream
->filename
, max_size
);
1215 /* parse http request and prepare header */
1216 static int http_parse_request(HTTPContext
*c
)
1219 enum RedirType redir_type
;
1221 char info
[1024], filename
[1024];
1225 const char *mime_type
;
1229 char *useragent
= 0;
1232 get_word(cmd
, sizeof(cmd
), (const char **)&p
);
1233 av_strlcpy(c
->method
, cmd
, sizeof(c
->method
));
1235 if (!strcmp(cmd
, "GET"))
1237 else if (!strcmp(cmd
, "POST"))
1242 get_word(url
, sizeof(url
), (const char **)&p
);
1243 av_strlcpy(c
->url
, url
, sizeof(c
->url
));
1245 get_word(protocol
, sizeof(protocol
), (const char **)&p
);
1246 if (strcmp(protocol
, "HTTP/1.0") && strcmp(protocol
, "HTTP/1.1"))
1249 av_strlcpy(c
->protocol
, protocol
, sizeof(c
->protocol
));
1252 http_log("New connection: %s %s\n", cmd
, url
);
1254 /* find the filename and the optional info string in the request */
1255 p
= strchr(url
, '?');
1257 av_strlcpy(info
, p
, sizeof(info
));
1262 av_strlcpy(filename
, url
+ ((*url
== '/') ? 1 : 0), sizeof(filename
)-1);
1264 for (p
= c
->buffer
; *p
&& *p
!= '\r' && *p
!= '\n'; ) {
1265 if (strncasecmp(p
, "User-Agent:", 11) == 0) {
1267 if (*useragent
&& *useragent
!= '\n' && isspace(*useragent
))
1271 p
= strchr(p
, '\n');
1278 redir_type
= REDIR_NONE
;
1279 if (match_ext(filename
, "asx")) {
1280 redir_type
= REDIR_ASX
;
1281 filename
[strlen(filename
)-1] = 'f';
1282 } else if (match_ext(filename
, "asf") &&
1283 (!useragent
|| strncasecmp(useragent
, "NSPlayer", 8) != 0)) {
1284 /* if this isn't WMP or lookalike, return the redirector file */
1285 redir_type
= REDIR_ASF
;
1286 } else if (match_ext(filename
, "rpm,ram")) {
1287 redir_type
= REDIR_RAM
;
1288 strcpy(filename
+ strlen(filename
)-2, "m");
1289 } else if (match_ext(filename
, "rtsp")) {
1290 redir_type
= REDIR_RTSP
;
1291 compute_real_filename(filename
, sizeof(filename
) - 1);
1292 } else if (match_ext(filename
, "sdp")) {
1293 redir_type
= REDIR_SDP
;
1294 compute_real_filename(filename
, sizeof(filename
) - 1);
1297 // "redirect" / request to index.html
1298 if (!strlen(filename
))
1299 av_strlcpy(filename
, "index.html", sizeof(filename
) - 1);
1301 stream
= first_stream
;
1302 while (stream
!= NULL
) {
1303 if (!strcmp(stream
->filename
, filename
) && validate_acl(stream
, c
))
1305 stream
= stream
->next
;
1307 if (stream
== NULL
) {
1308 snprintf(msg
, sizeof(msg
), "File '%s' not found", url
);
1313 memcpy(c
->feed_streams
, stream
->feed_streams
, sizeof(c
->feed_streams
));
1314 memset(c
->switch_feed_streams
, -1, sizeof(c
->switch_feed_streams
));
1316 if (stream
->stream_type
== STREAM_TYPE_REDIRECT
) {
1317 c
->http_error
= 301;
1319 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "HTTP/1.0 301 Moved\r\n");
1320 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "Location: %s\r\n", stream
->feed_filename
);
1321 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "Content-type: text/html\r\n");
1322 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "\r\n");
1323 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "<html><head><title>Moved</title></head><body>\r\n");
1324 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "You should be <a href=\"%s\">redirected</a>.\r\n", stream
->feed_filename
);
1325 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "</body></html>\r\n");
1327 /* prepare output buffer */
1328 c
->buffer_ptr
= c
->buffer
;
1330 c
->state
= HTTPSTATE_SEND_HEADER
;
1334 /* If this is WMP, get the rate information */
1335 if (extract_rates(ratebuf
, sizeof(ratebuf
), c
->buffer
)) {
1336 if (modify_current_stream(c
, ratebuf
)) {
1337 for (i
= 0; i
< sizeof(c
->feed_streams
) / sizeof(c
->feed_streams
[0]); i
++) {
1338 if (c
->switch_feed_streams
[i
] >= 0)
1339 do_switch_stream(c
, i
);
1344 /* If already streaming this feed, do not let start another feeder. */
1345 if (stream
->feed_opened
) {
1346 snprintf(msg
, sizeof(msg
), "This feed is already being received.");
1347 http_log("feed %s already being received\n", stream
->feed_filename
);
1351 if (c
->post
== 0 && stream
->stream_type
== STREAM_TYPE_LIVE
)
1352 current_bandwidth
+= stream
->bandwidth
;
1354 if (c
->post
== 0 && max_bandwidth
< current_bandwidth
) {
1355 c
->http_error
= 200;
1357 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "HTTP/1.0 200 Server too busy\r\n");
1358 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "Content-type: text/html\r\n");
1359 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "\r\n");
1360 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "<html><head><title>Too busy</title></head><body>\r\n");
1361 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");
1362 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",
1363 current_bandwidth
, max_bandwidth
);
1364 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "</body></html>\r\n");
1366 /* prepare output buffer */
1367 c
->buffer_ptr
= c
->buffer
;
1369 c
->state
= HTTPSTATE_SEND_HEADER
;
1373 if (redir_type
!= REDIR_NONE
) {
1376 for (p
= c
->buffer
; *p
&& *p
!= '\r' && *p
!= '\n'; ) {
1377 if (strncasecmp(p
, "Host:", 5) == 0) {
1381 p
= strchr(p
, '\n');
1392 while (isspace(*hostinfo
))
1395 eoh
= strchr(hostinfo
, '\n');
1397 if (eoh
[-1] == '\r')
1400 if (eoh
- hostinfo
< sizeof(hostbuf
) - 1) {
1401 memcpy(hostbuf
, hostinfo
, eoh
- hostinfo
);
1402 hostbuf
[eoh
- hostinfo
] = 0;
1404 c
->http_error
= 200;
1406 switch(redir_type
) {
1408 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "HTTP/1.0 200 ASX Follows\r\n");
1409 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "Content-type: video/x-ms-asf\r\n");
1410 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "\r\n");
1411 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "<ASX Version=\"3\">\r\n");
1412 //q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<!-- Autogenerated by ffserver -->\r\n");
1413 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n",
1414 hostbuf
, filename
, info
);
1415 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "</ASX>\r\n");
1418 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "HTTP/1.0 200 RAM Follows\r\n");
1419 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "Content-type: audio/x-pn-realaudio\r\n");
1420 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "\r\n");
1421 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "# Autogenerated by ffserver\r\n");
1422 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "http://%s/%s%s\r\n",
1423 hostbuf
, filename
, info
);
1426 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "HTTP/1.0 200 ASF Redirect follows\r\n");
1427 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "Content-type: video/x-ms-asf\r\n");
1428 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "\r\n");
1429 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "[Reference]\r\n");
1430 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "Ref1=http://%s/%s%s\r\n",
1431 hostbuf
, filename
, info
);
1435 char hostname
[256], *p
;
1436 /* extract only hostname */
1437 av_strlcpy(hostname
, hostbuf
, sizeof(hostname
));
1438 p
= strrchr(hostname
, ':');
1441 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "HTTP/1.0 200 RTSP Redirect follows\r\n");
1442 /* XXX: incorrect mime type ? */
1443 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "Content-type: application/x-rtsp\r\n");
1444 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "\r\n");
1445 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "rtsp://%s:%d/%s\r\n",
1446 hostname
, ntohs(my_rtsp_addr
.sin_port
),
1453 int sdp_data_size
, len
;
1454 struct sockaddr_in my_addr
;
1456 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "HTTP/1.0 200 OK\r\n");
1457 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "Content-type: application/sdp\r\n");
1458 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "\r\n");
1460 len
= sizeof(my_addr
);
1461 getsockname(c
->fd
, (struct sockaddr
*)&my_addr
, &len
);
1463 /* XXX: should use a dynamic buffer */
1464 sdp_data_size
= prepare_sdp_description(stream
,
1467 if (sdp_data_size
> 0) {
1468 memcpy(q
, sdp_data
, sdp_data_size
);
1480 /* prepare output buffer */
1481 c
->buffer_ptr
= c
->buffer
;
1483 c
->state
= HTTPSTATE_SEND_HEADER
;
1489 snprintf(msg
, sizeof(msg
), "ASX/RAM file not handled");
1493 stream
->conns_served
++;
1495 /* XXX: add there authenticate and IP match */
1498 /* if post, it means a feed is being sent */
1499 if (!stream
->is_feed
) {
1500 /* However it might be a status report from WMP! Lets log the data
1501 * as it might come in handy one day
1506 for (p
= c
->buffer
; *p
&& *p
!= '\r' && *p
!= '\n'; ) {
1507 if (strncasecmp(p
, "Pragma: log-line=", 17) == 0) {
1511 if (strncasecmp(p
, "Pragma: client-id=", 18) == 0)
1512 client_id
= strtol(p
+ 18, 0, 10);
1513 p
= strchr(p
, '\n');
1521 char *eol
= strchr(logline
, '\n');
1526 if (eol
[-1] == '\r')
1528 http_log("%.*s\n", (int) (eol
- logline
), logline
);
1529 c
->suppress_log
= 1;
1534 http_log("\nGot request:\n%s\n", c
->buffer
);
1537 if (client_id
&& extract_rates(ratebuf
, sizeof(ratebuf
), c
->buffer
)) {
1540 /* Now we have to find the client_id */
1541 for (wmpc
= first_http_ctx
; wmpc
; wmpc
= wmpc
->next
) {
1542 if (wmpc
->wmp_client_id
== client_id
)
1546 if (wmpc
&& modify_current_stream(wmpc
, ratebuf
))
1547 wmpc
->switch_pending
= 1;
1550 snprintf(msg
, sizeof(msg
), "POST command not handled");
1554 if (http_start_receive_data(c
) < 0) {
1555 snprintf(msg
, sizeof(msg
), "could not open feed");
1559 c
->state
= HTTPSTATE_RECEIVE_DATA
;
1564 if (strcmp(stream
->filename
+ strlen(stream
->filename
) - 4, ".asf") == 0)
1565 http_log("\nGot request:\n%s\n", c
->buffer
);
1568 if (c
->stream
->stream_type
== STREAM_TYPE_STATUS
)
1571 /* open input stream */
1572 if (open_input_stream(c
, info
) < 0) {
1573 snprintf(msg
, sizeof(msg
), "Input stream corresponding to '%s' not found", url
);
1577 /* prepare http header */
1579 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "HTTP/1.0 200 OK\r\n");
1580 mime_type
= c
->stream
->fmt
->mime_type
;
1582 mime_type
= "application/x-octet-stream";
1583 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "Pragma: no-cache\r\n");
1585 /* for asf, we need extra headers */
1586 if (!strcmp(c
->stream
->fmt
->name
,"asf_stream")) {
1587 /* Need to allocate a client id */
1589 c
->wmp_client_id
= av_random(&random_state
) & 0x7fffffff;
1591 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
);
1593 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "Content-Type: %s\r\n", mime_type
);
1594 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "\r\n");
1596 /* prepare output buffer */
1598 c
->buffer_ptr
= c
->buffer
;
1600 c
->state
= HTTPSTATE_SEND_HEADER
;
1603 c
->http_error
= 404;
1605 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "HTTP/1.0 404 Not Found\r\n");
1606 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "Content-type: %s\r\n", "text/html");
1607 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "\r\n");
1608 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "<HTML>\n");
1609 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n");
1610 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "<BODY>%s</BODY>\n", msg
);
1611 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "</HTML>\n");
1613 /* prepare output buffer */
1614 c
->buffer_ptr
= c
->buffer
;
1616 c
->state
= HTTPSTATE_SEND_HEADER
;
1620 c
->http_error
= 200; /* horrible : we use this value to avoid
1621 going to the send data state */
1622 c
->state
= HTTPSTATE_SEND_HEADER
;
1626 static void fmt_bytecount(ByteIOContext
*pb
, int64_t count
)
1628 static const char *suffix
= " kMGTP";
1631 for (s
= suffix
; count
>= 100000 && s
[1]; count
/= 1000, s
++);
1633 url_fprintf(pb
, "%"PRId64
"%c", count
, *s
);
1636 static void compute_status(HTTPContext
*c
)
1645 if (url_open_dyn_buf(&pb
) < 0) {
1646 /* XXX: return an error ? */
1647 c
->buffer_ptr
= c
->buffer
;
1648 c
->buffer_end
= c
->buffer
;
1652 url_fprintf(pb
, "HTTP/1.0 200 OK\r\n");
1653 url_fprintf(pb
, "Content-type: %s\r\n", "text/html");
1654 url_fprintf(pb
, "Pragma: no-cache\r\n");
1655 url_fprintf(pb
, "\r\n");
1657 url_fprintf(pb
, "<HEAD><TITLE>%s Status</TITLE>\n", program_name
);
1658 if (c
->stream
->feed_filename
[0])
1659 url_fprintf(pb
, "<link rel=\"shortcut icon\" href=\"%s\">\n", c
->stream
->feed_filename
);
1660 url_fprintf(pb
, "</HEAD>\n<BODY>");
1661 url_fprintf(pb
, "<H1>%s Status</H1>\n", program_name
);
1663 url_fprintf(pb
, "<H2>Available Streams</H2>\n");
1664 url_fprintf(pb
, "<TABLE cellspacing=0 cellpadding=4>\n");
1665 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");
1666 stream
= first_stream
;
1667 while (stream
!= NULL
) {
1668 char sfilename
[1024];
1671 if (stream
->feed
!= stream
) {
1672 av_strlcpy(sfilename
, stream
->filename
, sizeof(sfilename
) - 10);
1673 eosf
= sfilename
+ strlen(sfilename
);
1674 if (eosf
- sfilename
>= 4) {
1675 if (strcmp(eosf
- 4, ".asf") == 0)
1676 strcpy(eosf
- 4, ".asx");
1677 else if (strcmp(eosf
- 3, ".rm") == 0)
1678 strcpy(eosf
- 3, ".ram");
1679 else if (stream
->fmt
&& !strcmp(stream
->fmt
->name
, "rtp")) {
1680 /* generate a sample RTSP director if
1681 unicast. Generate an SDP redirector if
1683 eosf
= strrchr(sfilename
, '.');
1685 eosf
= sfilename
+ strlen(sfilename
);
1686 if (stream
->is_multicast
)
1687 strcpy(eosf
, ".sdp");
1689 strcpy(eosf
, ".rtsp");
1693 url_fprintf(pb
, "<TR><TD><A HREF=\"/%s\">%s</A> ",
1694 sfilename
, stream
->filename
);
1695 url_fprintf(pb
, "<td align=right> %d <td align=right> ",
1696 stream
->conns_served
);
1697 fmt_bytecount(pb
, stream
->bytes_served
);
1698 switch(stream
->stream_type
) {
1699 case STREAM_TYPE_LIVE
: {
1700 int audio_bit_rate
= 0;
1701 int video_bit_rate
= 0;
1702 const char *audio_codec_name
= "";
1703 const char *video_codec_name
= "";
1704 const char *audio_codec_name_extra
= "";
1705 const char *video_codec_name_extra
= "";
1707 for(i
=0;i
<stream
->nb_streams
;i
++) {
1708 AVStream
*st
= stream
->streams
[i
];
1709 AVCodec
*codec
= avcodec_find_encoder(st
->codec
->codec_id
);
1710 switch(st
->codec
->codec_type
) {
1711 case CODEC_TYPE_AUDIO
:
1712 audio_bit_rate
+= st
->codec
->bit_rate
;
1714 if (*audio_codec_name
)
1715 audio_codec_name_extra
= "...";
1716 audio_codec_name
= codec
->name
;
1719 case CODEC_TYPE_VIDEO
:
1720 video_bit_rate
+= st
->codec
->bit_rate
;
1722 if (*video_codec_name
)
1723 video_codec_name_extra
= "...";
1724 video_codec_name
= codec
->name
;
1727 case CODEC_TYPE_DATA
:
1728 video_bit_rate
+= st
->codec
->bit_rate
;
1734 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",
1737 video_bit_rate
/ 1000, video_codec_name
, video_codec_name_extra
,
1738 audio_bit_rate
/ 1000, audio_codec_name
, audio_codec_name_extra
);
1740 url_fprintf(pb
, "<TD>%s", stream
->feed
->filename
);
1742 url_fprintf(pb
, "<TD>%s", stream
->feed_filename
);
1743 url_fprintf(pb
, "\n");
1747 url_fprintf(pb
, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
1751 stream
= stream
->next
;
1753 url_fprintf(pb
, "</TABLE>\n");
1755 stream
= first_stream
;
1756 while (stream
!= NULL
) {
1757 if (stream
->feed
== stream
) {
1758 url_fprintf(pb
, "<h2>Feed %s</h2>", stream
->filename
);
1760 url_fprintf(pb
, "Running as pid %d.\n", stream
->pid
);
1762 #if defined(linux) && !defined(CONFIG_NOCUTILS)
1767 /* This is somewhat linux specific I guess */
1768 snprintf(ps_cmd
, sizeof(ps_cmd
),
1769 "ps -o \"%%cpu,cputime\" --no-headers %d",
1772 pid_stat
= popen(ps_cmd
, "r");
1777 if (fscanf(pid_stat
, "%10s %64s", cpuperc
,
1779 url_fprintf(pb
, "Currently using %s%% of the cpu. Total time used %s.\n",
1787 url_fprintf(pb
, "<p>");
1789 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");
1791 for (i
= 0; i
< stream
->nb_streams
; i
++) {
1792 AVStream
*st
= stream
->streams
[i
];
1793 AVCodec
*codec
= avcodec_find_encoder(st
->codec
->codec_id
);
1794 const char *type
= "unknown";
1795 char parameters
[64];
1799 switch(st
->codec
->codec_type
) {
1800 case CODEC_TYPE_AUDIO
:
1802 snprintf(parameters
, sizeof(parameters
), "%d channel(s), %d Hz", st
->codec
->channels
, st
->codec
->sample_rate
);
1804 case CODEC_TYPE_VIDEO
:
1806 snprintf(parameters
, sizeof(parameters
), "%dx%d, q=%d-%d, fps=%d", st
->codec
->width
, st
->codec
->height
,
1807 st
->codec
->qmin
, st
->codec
->qmax
, st
->codec
->time_base
.den
/ st
->codec
->time_base
.num
);
1812 url_fprintf(pb
, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
1813 i
, type
, st
->codec
->bit_rate
/1000, codec
? codec
->name
: "", parameters
);
1815 url_fprintf(pb
, "</table>\n");
1818 stream
= stream
->next
;
1824 AVCodecContext
*enc
;
1828 stream
= first_feed
;
1829 while (stream
!= NULL
) {
1830 url_fprintf(pb
, "<H1>Feed '%s'</H1>\n", stream
->filename
);
1831 url_fprintf(pb
, "<TABLE>\n");
1832 url_fprintf(pb
, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n");
1833 for(i
=0;i
<stream
->nb_streams
;i
++) {
1834 AVStream
*st
= stream
->streams
[i
];
1835 FeedData
*fdata
= st
->priv_data
;
1838 avcodec_string(buf
, sizeof(buf
), enc
);
1839 avg
= fdata
->avg_frame_size
* (float)enc
->rate
* 8.0;
1840 if (enc
->codec
->type
== CODEC_TYPE_AUDIO
&& enc
->frame_size
> 0)
1841 avg
/= enc
->frame_size
;
1842 url_fprintf(pb
, "<TR><TD>%s <TD> %d <TD> %"PRId64
" <TD> %0.1f\n",
1843 buf
, enc
->frame_number
, fdata
->data_count
, avg
/ 1000.0);
1845 url_fprintf(pb
, "</TABLE>\n");
1846 stream
= stream
->next_feed
;
1851 /* connection status */
1852 url_fprintf(pb
, "<H2>Connection Status</H2>\n");
1854 url_fprintf(pb
, "Number of connections: %d / %d<BR>\n",
1855 nb_connections
, nb_max_connections
);
1857 url_fprintf(pb
, "Bandwidth in use: %lldk / %lldk<BR>\n",
1858 current_bandwidth
, max_bandwidth
);
1860 url_fprintf(pb
, "<TABLE>\n");
1861 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");
1862 c1
= first_http_ctx
;
1864 while (c1
!= NULL
) {
1870 for (j
= 0; j
< c1
->stream
->nb_streams
; j
++) {
1871 if (!c1
->stream
->feed
)
1872 bitrate
+= c1
->stream
->streams
[j
]->codec
->bit_rate
;
1873 else if (c1
->feed_streams
[j
] >= 0)
1874 bitrate
+= c1
->stream
->feed
->streams
[c1
->feed_streams
[j
]]->codec
->bit_rate
;
1879 p
= inet_ntoa(c1
->from_addr
.sin_addr
);
1880 url_fprintf(pb
, "<TR><TD><B>%d</B><TD>%s%s<TD>%s<TD>%s<TD>%s<td align=right>",
1882 c1
->stream
? c1
->stream
->filename
: "",
1883 c1
->state
== HTTPSTATE_RECEIVE_DATA
? "(input)" : "",
1886 http_state
[c1
->state
]);
1887 fmt_bytecount(pb
, bitrate
);
1888 url_fprintf(pb
, "<td align=right>");
1889 fmt_bytecount(pb
, compute_datarate(&c1
->datarate
, c1
->data_count
) * 8);
1890 url_fprintf(pb
, "<td align=right>");
1891 fmt_bytecount(pb
, c1
->data_count
);
1892 url_fprintf(pb
, "\n");
1895 url_fprintf(pb
, "</TABLE>\n");
1900 url_fprintf(pb
, "<HR size=1 noshade>Generated at %s", p
);
1901 url_fprintf(pb
, "</BODY>\n</HTML>\n");
1903 len
= url_close_dyn_buf(pb
, &c
->pb_buffer
);
1904 c
->buffer_ptr
= c
->pb_buffer
;
1905 c
->buffer_end
= c
->pb_buffer
+ len
;
1908 /* check if the parser needs to be opened for stream i */
1909 static void open_parser(AVFormatContext
*s
, int i
)
1911 AVStream
*st
= s
->streams
[i
];
1914 if (!st
->codec
->codec
) {
1915 codec
= avcodec_find_decoder(st
->codec
->codec_id
);
1916 if (codec
&& (codec
->capabilities
& CODEC_CAP_PARSE_ONLY
)) {
1917 st
->codec
->parse_only
= 1;
1918 if (avcodec_open(st
->codec
, codec
) < 0)
1919 st
->codec
->parse_only
= 0;
1924 static int open_input_stream(HTTPContext
*c
, const char *info
)
1927 char input_filename
[1024];
1929 int buf_size
, i
, ret
;
1932 /* find file name */
1933 if (c
->stream
->feed
) {
1934 strcpy(input_filename
, c
->stream
->feed
->feed_filename
);
1935 buf_size
= FFM_PACKET_SIZE
;
1936 /* compute position (absolute time) */
1937 if (find_info_tag(buf
, sizeof(buf
), "date", info
)) {
1938 stream_pos
= parse_date(buf
, 0);
1939 if (stream_pos
== INT64_MIN
)
1941 } else if (find_info_tag(buf
, sizeof(buf
), "buffer", info
)) {
1942 int prebuffer
= strtol(buf
, 0, 10);
1943 stream_pos
= av_gettime() - prebuffer
* (int64_t)1000000;
1945 stream_pos
= av_gettime() - c
->stream
->prebuffer
* (int64_t)1000;
1947 strcpy(input_filename
, c
->stream
->feed_filename
);
1949 /* compute position (relative time) */
1950 if (find_info_tag(buf
, sizeof(buf
), "date", info
)) {
1951 stream_pos
= parse_date(buf
, 1);
1952 if (stream_pos
== INT64_MIN
)
1957 if (input_filename
[0] == '\0')
1961 { time_t when
= stream_pos
/ 1000000;
1962 http_log("Stream pos = %"PRId64
", time=%s", stream_pos
, ctime(&when
));
1967 if ((ret
= av_open_input_file(&s
, input_filename
, c
->stream
->ifmt
,
1968 buf_size
, c
->stream
->ap_in
)) < 0) {
1969 http_log("could not open %s: %d\n", input_filename
, ret
);
1972 s
->flags
|= AVFMT_FLAG_GENPTS
;
1974 av_find_stream_info(c
->fmt_in
);
1976 /* open each parser */
1977 for(i
=0;i
<s
->nb_streams
;i
++)
1980 /* choose stream as clock source (we favorize video stream if
1981 present) for packet sending */
1982 c
->pts_stream_index
= 0;
1983 for(i
=0;i
<c
->stream
->nb_streams
;i
++) {
1984 if (c
->pts_stream_index
== 0 &&
1985 c
->stream
->streams
[i
]->codec
->codec_type
== CODEC_TYPE_VIDEO
) {
1986 c
->pts_stream_index
= i
;
1991 if (c
->fmt_in
->iformat
->read_seek
)
1992 av_seek_frame(c
->fmt_in
, -1, stream_pos
, 0);
1994 /* set the start time (needed for maxtime and RTP packet timing) */
1995 c
->start_time
= cur_time
;
1996 c
->first_pts
= AV_NOPTS_VALUE
;
2000 /* return the server clock (in us) */
2001 static int64_t get_server_clock(HTTPContext
*c
)
2003 /* compute current pts value from system time */
2004 return (cur_time
- c
->start_time
) * 1000;
2007 /* return the estimated time at which the current packet must be sent
2009 static int64_t get_packet_send_clock(HTTPContext
*c
)
2011 int bytes_left
, bytes_sent
, frame_bytes
;
2013 frame_bytes
= c
->cur_frame_bytes
;
2014 if (frame_bytes
<= 0)
2017 bytes_left
= c
->buffer_end
- c
->buffer_ptr
;
2018 bytes_sent
= frame_bytes
- bytes_left
;
2019 return c
->cur_pts
+ (c
->cur_frame_duration
* bytes_sent
) / frame_bytes
;
2024 static int http_prepare_data(HTTPContext
*c
)
2027 AVFormatContext
*ctx
;
2029 av_freep(&c
->pb_buffer
);
2031 case HTTPSTATE_SEND_DATA_HEADER
:
2032 memset(&c
->fmt_ctx
, 0, sizeof(c
->fmt_ctx
));
2033 av_strlcpy(c
->fmt_ctx
.author
, c
->stream
->author
,
2034 sizeof(c
->fmt_ctx
.author
));
2035 av_strlcpy(c
->fmt_ctx
.comment
, c
->stream
->comment
,
2036 sizeof(c
->fmt_ctx
.comment
));
2037 av_strlcpy(c
->fmt_ctx
.copyright
, c
->stream
->copyright
,
2038 sizeof(c
->fmt_ctx
.copyright
));
2039 av_strlcpy(c
->fmt_ctx
.title
, c
->stream
->title
,
2040 sizeof(c
->fmt_ctx
.title
));
2042 for(i
=0;i
<c
->stream
->nb_streams
;i
++) {
2045 st
= av_mallocz(sizeof(AVStream
));
2046 c
->fmt_ctx
.streams
[i
] = st
;
2047 /* if file or feed, then just take streams from FFStream struct */
2048 if (!c
->stream
->feed
||
2049 c
->stream
->feed
== c
->stream
)
2050 src
= c
->stream
->streams
[i
];
2052 src
= c
->stream
->feed
->streams
[c
->stream
->feed_streams
[i
]];
2056 st
->codec
->frame_number
= 0; /* XXX: should be done in
2057 AVStream, not in codec */
2059 /* set output format parameters */
2060 c
->fmt_ctx
.oformat
= c
->stream
->fmt
;
2061 c
->fmt_ctx
.nb_streams
= c
->stream
->nb_streams
;
2063 c
->got_key_frame
= 0;
2065 /* prepare header and save header data in a stream */
2066 if (url_open_dyn_buf(&c
->fmt_ctx
.pb
) < 0) {
2067 /* XXX: potential leak */
2070 c
->fmt_ctx
.pb
->is_streamed
= 1;
2073 * HACK to avoid mpeg ps muxer to spit many underflow errors
2074 * Default value from FFmpeg
2075 * Try to set it use configuration option
2077 c
->fmt_ctx
.preload
= (int)(0.5*AV_TIME_BASE
);
2078 c
->fmt_ctx
.max_delay
= (int)(0.7*AV_TIME_BASE
);
2080 av_set_parameters(&c
->fmt_ctx
, NULL
);
2081 if (av_write_header(&c
->fmt_ctx
) < 0) {
2082 http_log("Error writing output header\n");
2086 len
= url_close_dyn_buf(c
->fmt_ctx
.pb
, &c
->pb_buffer
);
2087 c
->buffer_ptr
= c
->pb_buffer
;
2088 c
->buffer_end
= c
->pb_buffer
+ len
;
2090 c
->state
= HTTPSTATE_SEND_DATA
;
2091 c
->last_packet_sent
= 0;
2093 case HTTPSTATE_SEND_DATA
:
2094 /* find a new packet */
2095 /* read a packet from the input stream */
2096 if (c
->stream
->feed
)
2097 ffm_set_write_index(c
->fmt_in
,
2098 c
->stream
->feed
->feed_write_index
,
2099 c
->stream
->feed
->feed_size
);
2101 if (c
->stream
->max_time
&&
2102 c
->stream
->max_time
+ c
->start_time
- cur_time
< 0)
2103 /* We have timed out */
2104 c
->state
= HTTPSTATE_SEND_DATA_TRAILER
;
2108 if (av_read_frame(c
->fmt_in
, &pkt
) < 0) {
2109 if (c
->stream
->feed
&& c
->stream
->feed
->feed_opened
) {
2110 /* if coming from feed, it means we reached the end of the
2111 ffm file, so must wait for more data */
2112 c
->state
= HTTPSTATE_WAIT_FEED
;
2113 return 1; /* state changed */
2115 if (c
->stream
->loop
) {
2116 av_close_input_file(c
->fmt_in
);
2118 if (open_input_stream(c
, "") < 0)
2123 /* must send trailer now because eof or error */
2124 c
->state
= HTTPSTATE_SEND_DATA_TRAILER
;
2128 int source_index
= pkt
.stream_index
;
2129 /* update first pts if needed */
2130 if (c
->first_pts
== AV_NOPTS_VALUE
) {
2131 c
->first_pts
= av_rescale_q(pkt
.dts
, c
->fmt_in
->streams
[pkt
.stream_index
]->time_base
, AV_TIME_BASE_Q
);
2132 c
->start_time
= cur_time
;
2134 /* send it to the appropriate stream */
2135 if (c
->stream
->feed
) {
2136 /* if coming from a feed, select the right stream */
2137 if (c
->switch_pending
) {
2138 c
->switch_pending
= 0;
2139 for(i
=0;i
<c
->stream
->nb_streams
;i
++) {
2140 if (c
->switch_feed_streams
[i
] == pkt
.stream_index
)
2141 if (pkt
.flags
& PKT_FLAG_KEY
)
2142 do_switch_stream(c
, i
);
2143 if (c
->switch_feed_streams
[i
] >= 0)
2144 c
->switch_pending
= 1;
2147 for(i
=0;i
<c
->stream
->nb_streams
;i
++) {
2148 if (c
->feed_streams
[i
] == pkt
.stream_index
) {
2149 AVStream
*st
= c
->fmt_in
->streams
[source_index
];
2150 pkt
.stream_index
= i
;
2151 if (pkt
.flags
& PKT_FLAG_KEY
&&
2152 (st
->codec
->codec_type
== CODEC_TYPE_VIDEO
||
2153 c
->stream
->nb_streams
== 1))
2154 c
->got_key_frame
= 1;
2155 if (!c
->stream
->send_on_key
|| c
->got_key_frame
)
2160 AVCodecContext
*codec
;
2161 AVStream
*ist
, *ost
;
2163 ist
= c
->fmt_in
->streams
[source_index
];
2164 /* specific handling for RTP: we use several
2165 output stream (one for each RTP
2166 connection). XXX: need more abstract handling */
2167 if (c
->is_packetized
) {
2168 /* compute send time and duration */
2169 c
->cur_pts
= av_rescale_q(pkt
.dts
, ist
->time_base
, AV_TIME_BASE_Q
);
2170 if (ist
->start_time
!= AV_NOPTS_VALUE
)
2171 c
->cur_pts
-= av_rescale_q(ist
->start_time
, ist
->time_base
, AV_TIME_BASE_Q
);
2172 c
->cur_frame_duration
= av_rescale_q(pkt
.duration
, ist
->time_base
, AV_TIME_BASE_Q
);
2174 printf("index=%d pts=%0.3f duration=%0.6f\n",
2176 (double)c
->cur_pts
/
2178 (double)c
->cur_frame_duration
/
2181 /* find RTP context */
2182 c
->packet_stream_index
= pkt
.stream_index
;
2183 ctx
= c
->rtp_ctx
[c
->packet_stream_index
];
2185 av_free_packet(&pkt
);
2188 codec
= ctx
->streams
[0]->codec
;
2189 /* only one stream per RTP connection */
2190 pkt
.stream_index
= 0;
2194 codec
= ctx
->streams
[pkt
.stream_index
]->codec
;
2197 if (c
->is_packetized
) {
2198 int max_packet_size
;
2199 if (c
->rtp_protocol
== RTSP_PROTOCOL_RTP_TCP
)
2200 max_packet_size
= RTSP_TCP_MAX_PACKET_SIZE
;
2202 max_packet_size
= url_get_max_packet_size(c
->rtp_handles
[c
->packet_stream_index
]);
2203 ret
= url_open_dyn_packet_buf(&ctx
->pb
, max_packet_size
);
2205 ret
= url_open_dyn_buf(&ctx
->pb
);
2208 /* XXX: potential leak */
2211 ost
= ctx
->streams
[pkt
.stream_index
];
2213 ctx
->pb
->is_streamed
= 1;
2214 if (pkt
.dts
!= AV_NOPTS_VALUE
)
2215 pkt
.dts
= av_rescale_q(pkt
.dts
, ist
->time_base
, ost
->time_base
);
2216 if (pkt
.pts
!= AV_NOPTS_VALUE
)
2217 pkt
.pts
= av_rescale_q(pkt
.pts
, ist
->time_base
, ost
->time_base
);
2218 pkt
.duration
= av_rescale_q(pkt
.duration
, ist
->time_base
, ost
->time_base
);
2219 if (av_write_frame(ctx
, &pkt
) < 0) {
2220 http_log("Error writing frame to output\n");
2221 c
->state
= HTTPSTATE_SEND_DATA_TRAILER
;
2224 len
= url_close_dyn_buf(ctx
->pb
, &c
->pb_buffer
);
2225 c
->cur_frame_bytes
= len
;
2226 c
->buffer_ptr
= c
->pb_buffer
;
2227 c
->buffer_end
= c
->pb_buffer
+ len
;
2229 codec
->frame_number
++;
2231 av_free_packet(&pkt
);
2235 av_free_packet(&pkt
);
2240 case HTTPSTATE_SEND_DATA_TRAILER
:
2241 /* last packet test ? */
2242 if (c
->last_packet_sent
|| c
->is_packetized
)
2245 /* prepare header */
2246 if (url_open_dyn_buf(&ctx
->pb
) < 0) {
2247 /* XXX: potential leak */
2250 c
->fmt_ctx
.pb
->is_streamed
= 1;
2251 av_write_trailer(ctx
);
2252 len
= url_close_dyn_buf(ctx
->pb
, &c
->pb_buffer
);
2253 c
->buffer_ptr
= c
->pb_buffer
;
2254 c
->buffer_end
= c
->pb_buffer
+ len
;
2256 c
->last_packet_sent
= 1;
2262 /* should convert the format at the same time */
2263 /* send data starting at c->buffer_ptr to the output connection
2264 (either UDP or TCP connection) */
2265 static int http_send_data(HTTPContext
*c
)
2270 if (c
->buffer_ptr
>= c
->buffer_end
) {
2271 ret
= http_prepare_data(c
);
2275 /* state change requested */
2278 if (c
->is_packetized
) {
2279 /* RTP data output */
2280 len
= c
->buffer_end
- c
->buffer_ptr
;
2282 /* fail safe - should never happen */
2284 c
->buffer_ptr
= c
->buffer_end
;
2287 len
= (c
->buffer_ptr
[0] << 24) |
2288 (c
->buffer_ptr
[1] << 16) |
2289 (c
->buffer_ptr
[2] << 8) |
2291 if (len
> (c
->buffer_end
- c
->buffer_ptr
))
2293 if ((get_packet_send_clock(c
) - get_server_clock(c
)) > 0) {
2294 /* nothing to send yet: we can wait */
2298 c
->data_count
+= len
;
2299 update_datarate(&c
->datarate
, c
->data_count
);
2301 c
->stream
->bytes_served
+= len
;
2303 if (c
->rtp_protocol
== RTSP_PROTOCOL_RTP_TCP
) {
2304 /* RTP packets are sent inside the RTSP TCP connection */
2306 int interleaved_index
, size
;
2308 HTTPContext
*rtsp_c
;
2311 /* if no RTSP connection left, error */
2314 /* if already sending something, then wait. */
2315 if (rtsp_c
->state
!= RTSPSTATE_WAIT_REQUEST
)
2317 if (url_open_dyn_buf(&pb
) < 0)
2319 interleaved_index
= c
->packet_stream_index
* 2;
2320 /* RTCP packets are sent at odd indexes */
2321 if (c
->buffer_ptr
[1] == 200)
2322 interleaved_index
++;
2323 /* write RTSP TCP header */
2325 header
[1] = interleaved_index
;
2326 header
[2] = len
>> 8;
2328 put_buffer(pb
, header
, 4);
2329 /* write RTP packet data */
2331 put_buffer(pb
, c
->buffer_ptr
, len
);
2332 size
= url_close_dyn_buf(pb
, &c
->packet_buffer
);
2333 /* prepare asynchronous TCP sending */
2334 rtsp_c
->packet_buffer_ptr
= c
->packet_buffer
;
2335 rtsp_c
->packet_buffer_end
= c
->packet_buffer
+ size
;
2336 c
->buffer_ptr
+= len
;
2338 /* send everything we can NOW */
2339 len
= send(rtsp_c
->fd
, rtsp_c
->packet_buffer_ptr
,
2340 rtsp_c
->packet_buffer_end
- rtsp_c
->packet_buffer_ptr
, 0);
2342 rtsp_c
->packet_buffer_ptr
+= len
;
2343 if (rtsp_c
->packet_buffer_ptr
< rtsp_c
->packet_buffer_end
) {
2344 /* if we could not send all the data, we will
2345 send it later, so a new state is needed to
2346 "lock" the RTSP TCP connection */
2347 rtsp_c
->state
= RTSPSTATE_SEND_PACKET
;
2350 /* all data has been sent */
2351 av_freep(&c
->packet_buffer
);
2353 /* send RTP packet directly in UDP */
2355 url_write(c
->rtp_handles
[c
->packet_stream_index
],
2356 c
->buffer_ptr
, len
);
2357 c
->buffer_ptr
+= len
;
2358 /* here we continue as we can send several packets per 10 ms slot */
2361 /* TCP data output */
2362 len
= send(c
->fd
, c
->buffer_ptr
, c
->buffer_end
- c
->buffer_ptr
, 0);
2364 if (ff_neterrno() != FF_NETERROR(EAGAIN
) &&
2365 ff_neterrno() != FF_NETERROR(EINTR
))
2366 /* error : close connection */
2371 c
->buffer_ptr
+= len
;
2373 c
->data_count
+= len
;
2374 update_datarate(&c
->datarate
, c
->data_count
);
2376 c
->stream
->bytes_served
+= len
;
2384 static int http_start_receive_data(HTTPContext
*c
)
2388 if (c
->stream
->feed_opened
)
2391 /* Don't permit writing to this one */
2392 if (c
->stream
->readonly
)
2396 fd
= open(c
->stream
->feed_filename
, O_RDWR
);
2398 http_log("Error opening feeder file: %s\n", strerror(errno
));
2403 c
->stream
->feed_write_index
= ffm_read_write_index(fd
);
2404 c
->stream
->feed_size
= lseek(fd
, 0, SEEK_END
);
2405 lseek(fd
, 0, SEEK_SET
);
2407 /* init buffer input */
2408 c
->buffer_ptr
= c
->buffer
;
2409 c
->buffer_end
= c
->buffer
+ FFM_PACKET_SIZE
;
2410 c
->stream
->feed_opened
= 1;
2414 static int http_receive_data(HTTPContext
*c
)
2418 if (c
->buffer_end
> c
->buffer_ptr
) {
2421 len
= recv(c
->fd
, c
->buffer_ptr
, c
->buffer_end
- c
->buffer_ptr
, 0);
2423 if (ff_neterrno() != FF_NETERROR(EAGAIN
) &&
2424 ff_neterrno() != FF_NETERROR(EINTR
))
2425 /* error : close connection */
2427 } else if (len
== 0)
2428 /* end of connection : close it */
2431 c
->buffer_ptr
+= len
;
2432 c
->data_count
+= len
;
2433 update_datarate(&c
->datarate
, c
->data_count
);
2437 if (c
->buffer_ptr
- c
->buffer
>= 2 && c
->data_count
> FFM_PACKET_SIZE
) {
2438 if (c
->buffer
[0] != 'f' ||
2439 c
->buffer
[1] != 'm') {
2440 http_log("Feed stream has become desynchronized -- disconnecting\n");
2445 if (c
->buffer_ptr
>= c
->buffer_end
) {
2446 FFStream
*feed
= c
->stream
;
2447 /* a packet has been received : write it in the store, except
2449 if (c
->data_count
> FFM_PACKET_SIZE
) {
2451 // printf("writing pos=0x%"PRIx64" size=0x%"PRIx64"\n", feed->feed_write_index, feed->feed_size);
2452 /* XXX: use llseek or url_seek */
2453 lseek(c
->feed_fd
, feed
->feed_write_index
, SEEK_SET
);
2454 if (write(c
->feed_fd
, c
->buffer
, FFM_PACKET_SIZE
) < 0) {
2455 http_log("Error writing to feed file: %s\n", strerror(errno
));
2459 feed
->feed_write_index
+= FFM_PACKET_SIZE
;
2460 /* update file size */
2461 if (feed
->feed_write_index
> c
->stream
->feed_size
)
2462 feed
->feed_size
= feed
->feed_write_index
;
2464 /* handle wrap around if max file size reached */
2465 if (c
->stream
->feed_max_size
&& feed
->feed_write_index
>= c
->stream
->feed_max_size
)
2466 feed
->feed_write_index
= FFM_PACKET_SIZE
;
2469 ffm_write_write_index(c
->feed_fd
, feed
->feed_write_index
);
2471 /* wake up any waiting connections */
2472 for(c1
= first_http_ctx
; c1
!= NULL
; c1
= c1
->next
) {
2473 if (c1
->state
== HTTPSTATE_WAIT_FEED
&&
2474 c1
->stream
->feed
== c
->stream
->feed
)
2475 c1
->state
= HTTPSTATE_SEND_DATA
;
2478 /* We have a header in our hands that contains useful data */
2479 AVFormatContext
*s
= NULL
;
2481 AVInputFormat
*fmt_in
;
2484 /* use feed output format name to find corresponding input format */
2485 fmt_in
= av_find_input_format(feed
->fmt
->name
);
2489 url_open_buf(&pb
, c
->buffer
, c
->buffer_end
- c
->buffer
, URL_RDONLY
);
2490 pb
->is_streamed
= 1;
2492 if (av_open_input_stream(&s
, pb
, c
->stream
->feed_filename
, fmt_in
, NULL
) < 0) {
2497 /* Now we have the actual streams */
2498 if (s
->nb_streams
!= feed
->nb_streams
) {
2499 av_close_input_stream(s
);
2504 for (i
= 0; i
< s
->nb_streams
; i
++) {
2505 AVStream
*fst
= feed
->streams
[i
];
2506 AVStream
*st
= s
->streams
[i
];
2507 memcpy(fst
->codec
, st
->codec
, sizeof(AVCodecContext
));
2508 if (fst
->codec
->extradata_size
) {
2509 fst
->codec
->extradata
= av_malloc(fst
->codec
->extradata_size
);
2510 if (!fst
->codec
->extradata
)
2512 memcpy(fst
->codec
->extradata
, st
->codec
->extradata
,
2513 fst
->codec
->extradata_size
);
2517 av_close_input_stream(s
);
2520 c
->buffer_ptr
= c
->buffer
;
2525 c
->stream
->feed_opened
= 0;
2527 /* wake up any waiting connections to stop waiting for feed */
2528 for(c1
= first_http_ctx
; c1
!= NULL
; c1
= c1
->next
) {
2529 if (c1
->state
== HTTPSTATE_WAIT_FEED
&&
2530 c1
->stream
->feed
== c
->stream
->feed
)
2531 c1
->state
= HTTPSTATE_SEND_DATA_TRAILER
;
2536 /********************************************************************/
2539 static void rtsp_reply_header(HTTPContext
*c
, enum RTSPStatusCode error_number
)
2546 switch(error_number
) {
2547 case RTSP_STATUS_OK
:
2550 case RTSP_STATUS_METHOD
:
2551 str
= "Method Not Allowed";
2553 case RTSP_STATUS_BANDWIDTH
:
2554 str
= "Not Enough Bandwidth";
2556 case RTSP_STATUS_SESSION
:
2557 str
= "Session Not Found";
2559 case RTSP_STATUS_STATE
:
2560 str
= "Method Not Valid in This State";
2562 case RTSP_STATUS_AGGREGATE
:
2563 str
= "Aggregate operation not allowed";
2565 case RTSP_STATUS_ONLY_AGGREGATE
:
2566 str
= "Only aggregate operation allowed";
2568 case RTSP_STATUS_TRANSPORT
:
2569 str
= "Unsupported transport";
2571 case RTSP_STATUS_INTERNAL
:
2572 str
= "Internal Server Error";
2574 case RTSP_STATUS_SERVICE
:
2575 str
= "Service Unavailable";
2577 case RTSP_STATUS_VERSION
:
2578 str
= "RTSP Version not supported";
2581 str
= "Unknown Error";
2585 url_fprintf(c
->pb
, "RTSP/1.0 %d %s\r\n", error_number
, str
);
2586 url_fprintf(c
->pb
, "CSeq: %d\r\n", c
->seq
);
2588 /* output GMT time */
2592 p
= buf2
+ strlen(p
) - 1;
2595 url_fprintf(c
->pb
, "Date: %s GMT\r\n", buf2
);
2598 static void rtsp_reply_error(HTTPContext
*c
, enum RTSPStatusCode error_number
)
2600 rtsp_reply_header(c
, error_number
);
2601 url_fprintf(c
->pb
, "\r\n");
2604 static int rtsp_parse_request(HTTPContext
*c
)
2606 const char *p
, *p1
, *p2
;
2612 RTSPHeader header1
, *header
= &header1
;
2614 c
->buffer_ptr
[0] = '\0';
2617 get_word(cmd
, sizeof(cmd
), &p
);
2618 get_word(url
, sizeof(url
), &p
);
2619 get_word(protocol
, sizeof(protocol
), &p
);
2621 av_strlcpy(c
->method
, cmd
, sizeof(c
->method
));
2622 av_strlcpy(c
->url
, url
, sizeof(c
->url
));
2623 av_strlcpy(c
->protocol
, protocol
, sizeof(c
->protocol
));
2625 if (url_open_dyn_buf(&c
->pb
) < 0) {
2626 /* XXX: cannot do more */
2627 c
->pb
= NULL
; /* safety */
2631 /* check version name */
2632 if (strcmp(protocol
, "RTSP/1.0") != 0) {
2633 rtsp_reply_error(c
, RTSP_STATUS_VERSION
);
2637 /* parse each header line */
2638 memset(header
, 0, sizeof(RTSPHeader
));
2639 /* skip to next line */
2640 while (*p
!= '\n' && *p
!= '\0')
2644 while (*p
!= '\0') {
2645 p1
= strchr(p
, '\n');
2649 if (p2
> p
&& p2
[-1] == '\r')
2651 /* skip empty line */
2655 if (len
> sizeof(line
) - 1)
2656 len
= sizeof(line
) - 1;
2657 memcpy(line
, p
, len
);
2659 rtsp_parse_line(header
, line
);
2663 /* handle sequence number */
2664 c
->seq
= header
->seq
;
2666 if (!strcmp(cmd
, "DESCRIBE"))
2667 rtsp_cmd_describe(c
, url
);
2668 else if (!strcmp(cmd
, "OPTIONS"))
2669 rtsp_cmd_options(c
, url
);
2670 else if (!strcmp(cmd
, "SETUP"))
2671 rtsp_cmd_setup(c
, url
, header
);
2672 else if (!strcmp(cmd
, "PLAY"))
2673 rtsp_cmd_play(c
, url
, header
);
2674 else if (!strcmp(cmd
, "PAUSE"))
2675 rtsp_cmd_pause(c
, url
, header
);
2676 else if (!strcmp(cmd
, "TEARDOWN"))
2677 rtsp_cmd_teardown(c
, url
, header
);
2679 rtsp_reply_error(c
, RTSP_STATUS_METHOD
);
2682 len
= url_close_dyn_buf(c
->pb
, &c
->pb_buffer
);
2683 c
->pb
= NULL
; /* safety */
2685 /* XXX: cannot do more */
2688 c
->buffer_ptr
= c
->pb_buffer
;
2689 c
->buffer_end
= c
->pb_buffer
+ len
;
2690 c
->state
= RTSPSTATE_SEND_REPLY
;
2694 static int prepare_sdp_description(FFStream
*stream
, uint8_t **pbuffer
,
2695 struct in_addr my_ip
)
2697 AVFormatContext
*avc
;
2698 AVStream avs
[MAX_STREAMS
];
2701 avc
= av_alloc_format_context();
2705 if (stream
->title
[0] != 0) {
2706 av_strlcpy(avc
->title
, stream
->title
, sizeof(avc
->title
));
2708 av_strlcpy(avc
->title
, "No Title", sizeof(avc
->title
));
2710 avc
->nb_streams
= stream
->nb_streams
;
2711 if (stream
->is_multicast
) {
2712 snprintf(avc
->filename
, 1024, "rtp://%s:%d?multicast=1?ttl=%d",
2713 inet_ntoa(stream
->multicast_ip
),
2714 stream
->multicast_port
, stream
->multicast_ttl
);
2717 for(i
= 0; i
< stream
->nb_streams
; i
++) {
2718 avc
->streams
[i
] = &avs
[i
];
2719 avc
->streams
[i
]->codec
= stream
->streams
[i
]->codec
;
2721 *pbuffer
= av_mallocz(2048);
2722 avf_sdp_create(&avc
, 1, *pbuffer
, 2048);
2725 return strlen(*pbuffer
);
2728 static void rtsp_cmd_options(HTTPContext
*c
, const char *url
)
2730 // rtsp_reply_header(c, RTSP_STATUS_OK);
2731 url_fprintf(c
->pb
, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK
, "OK");
2732 url_fprintf(c
->pb
, "CSeq: %d\r\n", c
->seq
);
2733 url_fprintf(c
->pb
, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
2734 url_fprintf(c
->pb
, "\r\n");
2737 static void rtsp_cmd_describe(HTTPContext
*c
, const char *url
)
2743 int content_length
, len
;
2744 struct sockaddr_in my_addr
;
2746 /* find which url is asked */
2747 url_split(NULL
, 0, NULL
, 0, NULL
, 0, NULL
, path1
, sizeof(path1
), url
);
2752 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
2753 if (!stream
->is_feed
&&
2754 stream
->fmt
&& !strcmp(stream
->fmt
->name
, "rtp") &&
2755 !strcmp(path
, stream
->filename
)) {
2759 /* no stream found */
2760 rtsp_reply_error(c
, RTSP_STATUS_SERVICE
); /* XXX: right error ? */
2764 /* prepare the media description in sdp format */
2766 /* get the host IP */
2767 len
= sizeof(my_addr
);
2768 getsockname(c
->fd
, (struct sockaddr
*)&my_addr
, &len
);
2769 content_length
= prepare_sdp_description(stream
, &content
, my_addr
.sin_addr
);
2770 if (content_length
< 0) {
2771 rtsp_reply_error(c
, RTSP_STATUS_INTERNAL
);
2774 rtsp_reply_header(c
, RTSP_STATUS_OK
);
2775 url_fprintf(c
->pb
, "Content-Type: application/sdp\r\n");
2776 url_fprintf(c
->pb
, "Content-Length: %d\r\n", content_length
);
2777 url_fprintf(c
->pb
, "\r\n");
2778 put_buffer(c
->pb
, content
, content_length
);
2781 static HTTPContext
*find_rtp_session(const char *session_id
)
2785 if (session_id
[0] == '\0')
2788 for(c
= first_http_ctx
; c
!= NULL
; c
= c
->next
) {
2789 if (!strcmp(c
->session_id
, session_id
))
2795 static RTSPTransportField
*find_transport(RTSPHeader
*h
, enum RTSPProtocol protocol
)
2797 RTSPTransportField
*th
;
2800 for(i
=0;i
<h
->nb_transports
;i
++) {
2801 th
= &h
->transports
[i
];
2802 if (th
->protocol
== protocol
)
2808 static void rtsp_cmd_setup(HTTPContext
*c
, const char *url
,
2812 int stream_index
, port
;
2817 RTSPTransportField
*th
;
2818 struct sockaddr_in dest_addr
;
2819 RTSPActionServerSetup setup
;
2821 /* find which url is asked */
2822 url_split(NULL
, 0, NULL
, 0, NULL
, 0, NULL
, path1
, sizeof(path1
), url
);
2827 /* now check each stream */
2828 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
2829 if (!stream
->is_feed
&&
2830 stream
->fmt
&& !strcmp(stream
->fmt
->name
, "rtp")) {
2831 /* accept aggregate filenames only if single stream */
2832 if (!strcmp(path
, stream
->filename
)) {
2833 if (stream
->nb_streams
!= 1) {
2834 rtsp_reply_error(c
, RTSP_STATUS_AGGREGATE
);
2841 for(stream_index
= 0; stream_index
< stream
->nb_streams
;
2843 snprintf(buf
, sizeof(buf
), "%s/streamid=%d",
2844 stream
->filename
, stream_index
);
2845 if (!strcmp(path
, buf
))
2850 /* no stream found */
2851 rtsp_reply_error(c
, RTSP_STATUS_SERVICE
); /* XXX: right error ? */
2855 /* generate session id if needed */
2856 if (h
->session_id
[0] == '\0')
2857 snprintf(h
->session_id
, sizeof(h
->session_id
), "%08x%08x",
2858 av_random(&random_state
), av_random(&random_state
));
2860 /* find rtp session, and create it if none found */
2861 rtp_c
= find_rtp_session(h
->session_id
);
2863 /* always prefer UDP */
2864 th
= find_transport(h
, RTSP_PROTOCOL_RTP_UDP
);
2866 th
= find_transport(h
, RTSP_PROTOCOL_RTP_TCP
);
2868 rtsp_reply_error(c
, RTSP_STATUS_TRANSPORT
);
2873 rtp_c
= rtp_new_connection(&c
->from_addr
, stream
, h
->session_id
,
2876 rtsp_reply_error(c
, RTSP_STATUS_BANDWIDTH
);
2880 /* open input stream */
2881 if (open_input_stream(rtp_c
, "") < 0) {
2882 rtsp_reply_error(c
, RTSP_STATUS_INTERNAL
);
2887 /* test if stream is OK (test needed because several SETUP needs
2888 to be done for a given file) */
2889 if (rtp_c
->stream
!= stream
) {
2890 rtsp_reply_error(c
, RTSP_STATUS_SERVICE
);
2894 /* test if stream is already set up */
2895 if (rtp_c
->rtp_ctx
[stream_index
]) {
2896 rtsp_reply_error(c
, RTSP_STATUS_STATE
);
2900 /* check transport */
2901 th
= find_transport(h
, rtp_c
->rtp_protocol
);
2902 if (!th
|| (th
->protocol
== RTSP_PROTOCOL_RTP_UDP
&&
2903 th
->client_port_min
<= 0)) {
2904 rtsp_reply_error(c
, RTSP_STATUS_TRANSPORT
);
2908 /* setup default options */
2909 setup
.transport_option
[0] = '\0';
2910 dest_addr
= rtp_c
->from_addr
;
2911 dest_addr
.sin_port
= htons(th
->client_port_min
);
2914 if (rtp_new_av_stream(rtp_c
, stream_index
, &dest_addr
, c
) < 0) {
2915 rtsp_reply_error(c
, RTSP_STATUS_TRANSPORT
);
2919 /* now everything is OK, so we can send the connection parameters */
2920 rtsp_reply_header(c
, RTSP_STATUS_OK
);
2922 url_fprintf(c
->pb
, "Session: %s\r\n", rtp_c
->session_id
);
2924 switch(rtp_c
->rtp_protocol
) {
2925 case RTSP_PROTOCOL_RTP_UDP
:
2926 port
= rtp_get_local_port(rtp_c
->rtp_handles
[stream_index
]);
2927 url_fprintf(c
->pb
, "Transport: RTP/AVP/UDP;unicast;"
2928 "client_port=%d-%d;server_port=%d-%d",
2929 th
->client_port_min
, th
->client_port_min
+ 1,
2932 case RTSP_PROTOCOL_RTP_TCP
:
2933 url_fprintf(c
->pb
, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
2934 stream_index
* 2, stream_index
* 2 + 1);
2939 if (setup
.transport_option
[0] != '\0')
2940 url_fprintf(c
->pb
, ";%s", setup
.transport_option
);
2941 url_fprintf(c
->pb
, "\r\n");
2944 url_fprintf(c
->pb
, "\r\n");
2948 /* find an rtp connection by using the session ID. Check consistency
2950 static HTTPContext
*find_rtp_session_with_url(const char *url
,
2951 const char *session_id
)
2959 rtp_c
= find_rtp_session(session_id
);
2963 /* find which url is asked */
2964 url_split(NULL
, 0, NULL
, 0, NULL
, 0, NULL
, path1
, sizeof(path1
), url
);
2968 if(!strcmp(path
, rtp_c
->stream
->filename
)) return rtp_c
;
2969 for(s
=0; s
<rtp_c
->stream
->nb_streams
; ++s
) {
2970 snprintf(buf
, sizeof(buf
), "%s/streamid=%d",
2971 rtp_c
->stream
->filename
, s
);
2972 if(!strncmp(path
, buf
, sizeof(buf
))) {
2973 // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
2980 static void rtsp_cmd_play(HTTPContext
*c
, const char *url
, RTSPHeader
*h
)
2984 rtp_c
= find_rtp_session_with_url(url
, h
->session_id
);
2986 rtsp_reply_error(c
, RTSP_STATUS_SESSION
);
2990 if (rtp_c
->state
!= HTTPSTATE_SEND_DATA
&&
2991 rtp_c
->state
!= HTTPSTATE_WAIT_FEED
&&
2992 rtp_c
->state
!= HTTPSTATE_READY
) {
2993 rtsp_reply_error(c
, RTSP_STATUS_STATE
);
2998 /* XXX: seek in stream */
2999 if (h
->range_start
!= AV_NOPTS_VALUE
) {
3000 printf("range_start=%0.3f\n", (double)h
->range_start
/ AV_TIME_BASE
);
3001 av_seek_frame(rtp_c
->fmt_in
, -1, h
->range_start
);
3005 rtp_c
->state
= HTTPSTATE_SEND_DATA
;
3007 /* now everything is OK, so we can send the connection parameters */
3008 rtsp_reply_header(c
, RTSP_STATUS_OK
);
3010 url_fprintf(c
->pb
, "Session: %s\r\n", rtp_c
->session_id
);
3011 url_fprintf(c
->pb
, "\r\n");
3014 static void rtsp_cmd_pause(HTTPContext
*c
, const char *url
, RTSPHeader
*h
)
3018 rtp_c
= find_rtp_session_with_url(url
, h
->session_id
);
3020 rtsp_reply_error(c
, RTSP_STATUS_SESSION
);
3024 if (rtp_c
->state
!= HTTPSTATE_SEND_DATA
&&
3025 rtp_c
->state
!= HTTPSTATE_WAIT_FEED
) {
3026 rtsp_reply_error(c
, RTSP_STATUS_STATE
);
3030 rtp_c
->state
= HTTPSTATE_READY
;
3031 rtp_c
->first_pts
= AV_NOPTS_VALUE
;
3032 /* now everything is OK, so we can send the connection parameters */
3033 rtsp_reply_header(c
, RTSP_STATUS_OK
);
3035 url_fprintf(c
->pb
, "Session: %s\r\n", rtp_c
->session_id
);
3036 url_fprintf(c
->pb
, "\r\n");
3039 static void rtsp_cmd_teardown(HTTPContext
*c
, const char *url
, RTSPHeader
*h
)
3042 char session_id
[32];
3044 rtp_c
= find_rtp_session_with_url(url
, h
->session_id
);
3046 rtsp_reply_error(c
, RTSP_STATUS_SESSION
);
3050 av_strlcpy(session_id
, rtp_c
->session_id
, sizeof(session_id
));
3052 /* abort the session */
3053 close_connection(rtp_c
);
3055 /* now everything is OK, so we can send the connection parameters */
3056 rtsp_reply_header(c
, RTSP_STATUS_OK
);
3058 url_fprintf(c
->pb
, "Session: %s\r\n", session_id
);
3059 url_fprintf(c
->pb
, "\r\n");
3063 /********************************************************************/
3066 static HTTPContext
*rtp_new_connection(struct sockaddr_in
*from_addr
,
3067 FFStream
*stream
, const char *session_id
,
3068 enum RTSPProtocol rtp_protocol
)
3070 HTTPContext
*c
= NULL
;
3071 const char *proto_str
;
3073 /* XXX: should output a warning page when coming
3074 close to the connection limit */
3075 if (nb_connections
>= nb_max_connections
)
3078 /* add a new connection */
3079 c
= av_mallocz(sizeof(HTTPContext
));
3084 c
->poll_entry
= NULL
;
3085 c
->from_addr
= *from_addr
;
3086 c
->buffer_size
= IOBUFFER_INIT_SIZE
;
3087 c
->buffer
= av_malloc(c
->buffer_size
);
3092 av_strlcpy(c
->session_id
, session_id
, sizeof(c
->session_id
));
3093 c
->state
= HTTPSTATE_READY
;
3094 c
->is_packetized
= 1;
3095 c
->rtp_protocol
= rtp_protocol
;
3097 /* protocol is shown in statistics */
3098 switch(c
->rtp_protocol
) {
3099 case RTSP_PROTOCOL_RTP_UDP_MULTICAST
:
3100 proto_str
= "MCAST";
3102 case RTSP_PROTOCOL_RTP_UDP
:
3105 case RTSP_PROTOCOL_RTP_TCP
:
3112 av_strlcpy(c
->protocol
, "RTP/", sizeof(c
->protocol
));
3113 av_strlcat(c
->protocol
, proto_str
, sizeof(c
->protocol
));
3115 current_bandwidth
+= stream
->bandwidth
;
3117 c
->next
= first_http_ctx
;
3129 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3130 command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
3132 static int rtp_new_av_stream(HTTPContext
*c
,
3133 int stream_index
, struct sockaddr_in
*dest_addr
,
3134 HTTPContext
*rtsp_c
)
3136 AVFormatContext
*ctx
;
3139 URLContext
*h
= NULL
;
3141 int max_packet_size
;
3143 /* now we can open the relevant output stream */
3144 ctx
= av_alloc_format_context();
3147 ctx
->oformat
= guess_format("rtp", NULL
, NULL
);
3149 st
= av_mallocz(sizeof(AVStream
));
3152 st
->codec
= avcodec_alloc_context();
3153 ctx
->nb_streams
= 1;
3154 ctx
->streams
[0] = st
;
3156 if (!c
->stream
->feed
||
3157 c
->stream
->feed
== c
->stream
)
3158 memcpy(st
, c
->stream
->streams
[stream_index
], sizeof(AVStream
));
3161 c
->stream
->feed
->streams
[c
->stream
->feed_streams
[stream_index
]],
3163 st
->priv_data
= NULL
;
3165 /* build destination RTP address */
3166 ipaddr
= inet_ntoa(dest_addr
->sin_addr
);
3168 switch(c
->rtp_protocol
) {
3169 case RTSP_PROTOCOL_RTP_UDP
:
3170 case RTSP_PROTOCOL_RTP_UDP_MULTICAST
:
3173 /* XXX: also pass as parameter to function ? */
3174 if (c
->stream
->is_multicast
) {
3176 ttl
= c
->stream
->multicast_ttl
;
3179 snprintf(ctx
->filename
, sizeof(ctx
->filename
),
3180 "rtp://%s:%d?multicast=1&ttl=%d",
3181 ipaddr
, ntohs(dest_addr
->sin_port
), ttl
);
3183 snprintf(ctx
->filename
, sizeof(ctx
->filename
),
3184 "rtp://%s:%d", ipaddr
, ntohs(dest_addr
->sin_port
));
3187 if (url_open(&h
, ctx
->filename
, URL_WRONLY
) < 0)
3189 c
->rtp_handles
[stream_index
] = h
;
3190 max_packet_size
= url_get_max_packet_size(h
);
3192 case RTSP_PROTOCOL_RTP_TCP
:
3195 max_packet_size
= RTSP_TCP_MAX_PACKET_SIZE
;
3201 http_log("%s:%d - - \"PLAY %s/streamid=%d %s\"\n",
3202 ipaddr
, ntohs(dest_addr
->sin_port
),
3203 c
->stream
->filename
, stream_index
, c
->protocol
);
3205 /* normally, no packets should be output here, but the packet size may be checked */
3206 if (url_open_dyn_packet_buf(&ctx
->pb
, max_packet_size
) < 0) {
3207 /* XXX: close stream */
3210 av_set_parameters(ctx
, NULL
);
3211 if (av_write_header(ctx
) < 0) {
3218 url_close_dyn_buf(ctx
->pb
, &dummy_buf
);
3221 c
->rtp_ctx
[stream_index
] = ctx
;
3225 /********************************************************************/
3226 /* ffserver initialization */
3228 static AVStream
*add_av_stream1(FFStream
*stream
, AVCodecContext
*codec
)
3232 fst
= av_mallocz(sizeof(AVStream
));
3235 fst
->codec
= avcodec_alloc_context();
3236 fst
->priv_data
= av_mallocz(sizeof(FeedData
));
3237 memcpy(fst
->codec
, codec
, sizeof(AVCodecContext
));
3238 fst
->index
= stream
->nb_streams
;
3239 av_set_pts_info(fst
, 33, 1, 90000);
3240 stream
->streams
[stream
->nb_streams
++] = fst
;
3244 /* return the stream number in the feed */
3245 static int add_av_stream(FFStream
*feed
, AVStream
*st
)
3248 AVCodecContext
*av
, *av1
;
3252 for(i
=0;i
<feed
->nb_streams
;i
++) {
3253 st
= feed
->streams
[i
];
3255 if (av1
->codec_id
== av
->codec_id
&&
3256 av1
->codec_type
== av
->codec_type
&&
3257 av1
->bit_rate
== av
->bit_rate
) {
3259 switch(av
->codec_type
) {
3260 case CODEC_TYPE_AUDIO
:
3261 if (av1
->channels
== av
->channels
&&
3262 av1
->sample_rate
== av
->sample_rate
)
3265 case CODEC_TYPE_VIDEO
:
3266 if (av1
->width
== av
->width
&&
3267 av1
->height
== av
->height
&&
3268 av1
->time_base
.den
== av
->time_base
.den
&&
3269 av1
->time_base
.num
== av
->time_base
.num
&&
3270 av1
->gop_size
== av
->gop_size
)
3279 fst
= add_av_stream1(feed
, av
);
3282 return feed
->nb_streams
- 1;
3287 static void remove_stream(FFStream
*stream
)
3291 while (*ps
!= NULL
) {
3299 /* specific mpeg4 handling : we extract the raw parameters */
3300 static void extract_mpeg4_header(AVFormatContext
*infile
)
3302 int mpeg4_count
, i
, size
;
3308 for(i
=0;i
<infile
->nb_streams
;i
++) {
3309 st
= infile
->streams
[i
];
3310 if (st
->codec
->codec_id
== CODEC_ID_MPEG4
&&
3311 st
->codec
->extradata_size
== 0) {
3318 printf("MPEG4 without extra data: trying to find header in %s\n", infile
->filename
);
3319 while (mpeg4_count
> 0) {
3320 if (av_read_packet(infile
, &pkt
) < 0)
3322 st
= infile
->streams
[pkt
.stream_index
];
3323 if (st
->codec
->codec_id
== CODEC_ID_MPEG4
&&
3324 st
->codec
->extradata_size
== 0) {
3325 av_freep(&st
->codec
->extradata
);
3326 /* fill extradata with the header */
3327 /* XXX: we make hard suppositions here ! */
3329 while (p
< pkt
.data
+ pkt
.size
- 4) {
3330 /* stop when vop header is found */
3331 if (p
[0] == 0x00 && p
[1] == 0x00 &&
3332 p
[2] == 0x01 && p
[3] == 0xb6) {
3333 size
= p
- pkt
.data
;
3334 // av_hex_dump_log(infile, AV_LOG_DEBUG, pkt.data, size);
3335 st
->codec
->extradata
= av_malloc(size
);
3336 st
->codec
->extradata_size
= size
;
3337 memcpy(st
->codec
->extradata
, pkt
.data
, size
);
3344 av_free_packet(&pkt
);
3348 /* compute the needed AVStream for each file */
3349 static void build_file_streams(void)
3351 FFStream
*stream
, *stream_next
;
3352 AVFormatContext
*infile
;
3355 /* gather all streams */
3356 for(stream
= first_stream
; stream
!= NULL
; stream
= stream_next
) {
3357 stream_next
= stream
->next
;
3358 if (stream
->stream_type
== STREAM_TYPE_LIVE
&&
3360 /* the stream comes from a file */
3361 /* try to open the file */
3363 stream
->ap_in
= av_mallocz(sizeof(AVFormatParameters
));
3364 if (stream
->fmt
&& !strcmp(stream
->fmt
->name
, "rtp")) {
3365 /* specific case : if transport stream output to RTP,
3366 we use a raw transport stream reader */
3367 stream
->ap_in
->mpeg2ts_raw
= 1;
3368 stream
->ap_in
->mpeg2ts_compute_pcr
= 1;
3371 if ((ret
= av_open_input_file(&infile
, stream
->feed_filename
,
3372 stream
->ifmt
, 0, stream
->ap_in
)) < 0) {
3373 http_log("could not open %s: %d\n", stream
->feed_filename
, ret
);
3374 /* remove stream (no need to spend more time on it) */
3376 remove_stream(stream
);
3378 /* find all the AVStreams inside and reference them in
3380 if (av_find_stream_info(infile
) < 0) {
3381 http_log("Could not find codec parameters from '%s'\n",
3382 stream
->feed_filename
);
3383 av_close_input_file(infile
);
3386 extract_mpeg4_header(infile
);
3388 for(i
=0;i
<infile
->nb_streams
;i
++)
3389 add_av_stream1(stream
, infile
->streams
[i
]->codec
);
3391 av_close_input_file(infile
);
3397 /* compute the needed AVStream for each feed */
3398 static void build_feed_streams(void)
3400 FFStream
*stream
, *feed
;
3403 /* gather all streams */
3404 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
3405 feed
= stream
->feed
;
3407 if (!stream
->is_feed
) {
3408 /* we handle a stream coming from a feed */
3409 for(i
=0;i
<stream
->nb_streams
;i
++)
3410 stream
->feed_streams
[i
] = add_av_stream(feed
, stream
->streams
[i
]);
3415 /* gather all streams */
3416 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
3417 feed
= stream
->feed
;
3419 if (stream
->is_feed
) {
3420 for(i
=0;i
<stream
->nb_streams
;i
++)
3421 stream
->feed_streams
[i
] = i
;
3426 /* create feed files if needed */
3427 for(feed
= first_feed
; feed
!= NULL
; feed
= feed
->next_feed
) {
3430 if (url_exist(feed
->feed_filename
)) {
3431 /* See if it matches */
3435 if (av_open_input_file(&s
, feed
->feed_filename
, NULL
, FFM_PACKET_SIZE
, NULL
) >= 0) {
3436 /* Now see if it matches */
3437 if (s
->nb_streams
== feed
->nb_streams
) {
3439 for(i
=0;i
<s
->nb_streams
;i
++) {
3441 sf
= feed
->streams
[i
];
3444 if (sf
->index
!= ss
->index
||
3446 http_log("Index & Id do not match for stream %d (%s)\n",
3447 i
, feed
->feed_filename
);
3450 AVCodecContext
*ccf
, *ccs
;
3454 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3456 if (CHECK_CODEC(codec
) || CHECK_CODEC(codec_type
)) {
3457 http_log("Codecs do not match for stream %d\n", i
);
3459 } else if (CHECK_CODEC(bit_rate
) || CHECK_CODEC(flags
)) {
3460 http_log("Codec bitrates do not match for stream %d\n", i
);
3462 } else if (ccf
->codec_type
== CODEC_TYPE_VIDEO
) {
3463 if (CHECK_CODEC(time_base
.den
) ||
3464 CHECK_CODEC(time_base
.num
) ||
3465 CHECK_CODEC(width
) ||
3466 CHECK_CODEC(height
)) {
3467 http_log("Codec width, height and framerate do not match for stream %d\n", i
);
3470 } else if (ccf
->codec_type
== CODEC_TYPE_AUDIO
) {
3471 if (CHECK_CODEC(sample_rate
) ||
3472 CHECK_CODEC(channels
) ||
3473 CHECK_CODEC(frame_size
)) {
3474 http_log("Codec sample_rate, channels, frame_size do not match for stream %d\n", i
);
3478 http_log("Unknown codec type\n");
3486 http_log("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3487 feed
->feed_filename
, s
->nb_streams
, feed
->nb_streams
);
3489 av_close_input_file(s
);
3491 http_log("Deleting feed file '%s' as it appears to be corrupt\n",
3492 feed
->feed_filename
);
3495 if (feed
->readonly
) {
3496 http_log("Unable to delete feed file '%s' as it is marked readonly\n",
3497 feed
->feed_filename
);
3500 unlink(feed
->feed_filename
);
3503 if (!url_exist(feed
->feed_filename
)) {
3504 AVFormatContext s1
, *s
= &s1
;
3506 if (feed
->readonly
) {
3507 http_log("Unable to create feed file '%s' as it is marked readonly\n",
3508 feed
->feed_filename
);
3512 /* only write the header of the ffm file */
3513 if (url_fopen(&s
->pb
, feed
->feed_filename
, URL_WRONLY
) < 0) {
3514 http_log("Could not open output feed file '%s'\n",
3515 feed
->feed_filename
);
3518 s
->oformat
= feed
->fmt
;
3519 s
->nb_streams
= feed
->nb_streams
;
3520 for(i
=0;i
<s
->nb_streams
;i
++) {
3522 st
= feed
->streams
[i
];
3525 av_set_parameters(s
, NULL
);
3526 if (av_write_header(s
) < 0) {
3527 http_log("Container doesn't supports the required parameters\n");
3530 /* XXX: need better api */
3531 av_freep(&s
->priv_data
);
3534 /* get feed size and write index */
3535 fd
= open(feed
->feed_filename
, O_RDONLY
);
3537 http_log("Could not open output feed file '%s'\n",
3538 feed
->feed_filename
);
3542 feed
->feed_write_index
= ffm_read_write_index(fd
);
3543 feed
->feed_size
= lseek(fd
, 0, SEEK_END
);
3544 /* ensure that we do not wrap before the end of file */
3545 if (feed
->feed_max_size
&& feed
->feed_max_size
< feed
->feed_size
)
3546 feed
->feed_max_size
= feed
->feed_size
;
3552 /* compute the bandwidth used by each stream */
3553 static void compute_bandwidth(void)
3559 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
3561 for(i
=0;i
<stream
->nb_streams
;i
++) {
3562 AVStream
*st
= stream
->streams
[i
];
3563 switch(st
->codec
->codec_type
) {
3564 case CODEC_TYPE_AUDIO
:
3565 case CODEC_TYPE_VIDEO
:
3566 bandwidth
+= st
->codec
->bit_rate
;
3572 stream
->bandwidth
= (bandwidth
+ 999) / 1000;
3576 static void get_arg(char *buf
, int buf_size
, const char **pp
)
3583 while (isspace(*p
)) p
++;
3586 if (*p
== '\"' || *p
== '\'')
3598 if ((q
- buf
) < buf_size
- 1)
3603 if (quote
&& *p
== quote
)
3608 /* add a codec and set the default parameters */
3609 static void add_codec(FFStream
*stream
, AVCodecContext
*av
)
3613 /* compute default parameters */
3614 switch(av
->codec_type
) {
3615 case CODEC_TYPE_AUDIO
:
3616 if (av
->bit_rate
== 0)
3617 av
->bit_rate
= 64000;
3618 if (av
->sample_rate
== 0)
3619 av
->sample_rate
= 22050;
3620 if (av
->channels
== 0)
3623 case CODEC_TYPE_VIDEO
:
3624 if (av
->bit_rate
== 0)
3625 av
->bit_rate
= 64000;
3626 if (av
->time_base
.num
== 0){
3627 av
->time_base
.den
= 5;
3628 av
->time_base
.num
= 1;
3630 if (av
->width
== 0 || av
->height
== 0) {
3634 /* Bitrate tolerance is less for streaming */
3635 if (av
->bit_rate_tolerance
== 0)
3636 av
->bit_rate_tolerance
= FFMAX(av
->bit_rate
/ 4,
3637 (int64_t)av
->bit_rate
*av
->time_base
.num
/av
->time_base
.den
);
3642 if (av
->max_qdiff
== 0)
3644 av
->qcompress
= 0.5;
3647 if (!av
->nsse_weight
)
3648 av
->nsse_weight
= 8;
3650 av
->frame_skip_cmp
= FF_CMP_DCTMAX
;
3651 av
->me_method
= ME_EPZS
;
3652 av
->rc_buffer_aggressivity
= 1.0;
3655 av
->rc_eq
= "tex^qComp";
3656 if (!av
->i_quant_factor
)
3657 av
->i_quant_factor
= -0.8;
3658 if (!av
->b_quant_factor
)
3659 av
->b_quant_factor
= 1.25;
3660 if (!av
->b_quant_offset
)
3661 av
->b_quant_offset
= 1.25;
3662 if (!av
->rc_max_rate
)
3663 av
->rc_max_rate
= av
->bit_rate
* 2;
3665 if (av
->rc_max_rate
&& !av
->rc_buffer_size
) {
3666 av
->rc_buffer_size
= av
->rc_max_rate
;
3675 st
= av_mallocz(sizeof(AVStream
));
3678 st
->codec
= avcodec_alloc_context();
3679 stream
->streams
[stream
->nb_streams
++] = st
;
3680 memcpy(st
->codec
, av
, sizeof(AVCodecContext
));
3683 static int opt_audio_codec(const char *arg
)
3685 AVCodec
*p
= avcodec_find_encoder_by_name(arg
);
3687 if (p
== NULL
|| p
->type
!= CODEC_TYPE_AUDIO
)
3688 return CODEC_ID_NONE
;
3693 static int opt_video_codec(const char *arg
)
3695 AVCodec
*p
= avcodec_find_encoder_by_name(arg
);
3697 if (p
== NULL
|| p
->type
!= CODEC_TYPE_VIDEO
)
3698 return CODEC_ID_NONE
;
3703 /* simplistic plugin support */
3706 static void load_module(const char *filename
)
3709 void (*init_func
)(void);
3710 dll
= dlopen(filename
, RTLD_NOW
);
3712 fprintf(stderr
, "Could not load module '%s' - %s\n",
3713 filename
, dlerror());
3717 init_func
= dlsym(dll
, "ffserver_module_init");
3720 "%s: init function 'ffserver_module_init()' not found\n",
3729 static int opt_default(const char *opt
, const char *arg
,
3730 AVCodecContext
*avctx
, int type
)
3732 const AVOption
*o
= NULL
;
3733 const AVOption
*o2
= av_find_opt(avctx
, opt
, NULL
, type
, type
);
3735 o
= av_set_string2(avctx
, opt
, arg
, 1);
3741 static int parse_ffconfig(const char *filename
)
3748 int val
, errors
, line_num
;
3749 FFStream
**last_stream
, *stream
, *redirect
;
3750 FFStream
**last_feed
, *feed
;
3751 AVCodecContext audio_enc
, video_enc
;
3752 int audio_id
, video_id
;
3754 f
= fopen(filename
, "r");
3762 first_stream
= NULL
;
3763 last_stream
= &first_stream
;
3765 last_feed
= &first_feed
;
3769 audio_id
= CODEC_ID_NONE
;
3770 video_id
= CODEC_ID_NONE
;
3772 if (fgets(line
, sizeof(line
), f
) == NULL
)
3778 if (*p
== '\0' || *p
== '#')
3781 get_arg(cmd
, sizeof(cmd
), &p
);
3783 if (!strcasecmp(cmd
, "Port")) {
3784 get_arg(arg
, sizeof(arg
), &p
);
3786 if (val
< 1 || val
> 65536) {
3787 fprintf(stderr
, "%s:%d: Invalid port: %s\n",
3788 filename
, line_num
, arg
);
3791 my_http_addr
.sin_port
= htons(val
);
3792 } else if (!strcasecmp(cmd
, "BindAddress")) {
3793 get_arg(arg
, sizeof(arg
), &p
);
3794 if (resolve_host(&my_http_addr
.sin_addr
, arg
) != 0) {
3795 fprintf(stderr
, "%s:%d: Invalid host/IP address: %s\n",
3796 filename
, line_num
, arg
);
3799 } else if (!strcasecmp(cmd
, "NoDaemon")) {
3800 ffserver_daemon
= 0;
3801 } else if (!strcasecmp(cmd
, "RTSPPort")) {
3802 get_arg(arg
, sizeof(arg
), &p
);
3804 if (val
< 1 || val
> 65536) {
3805 fprintf(stderr
, "%s:%d: Invalid port: %s\n",
3806 filename
, line_num
, arg
);
3809 my_rtsp_addr
.sin_port
= htons(atoi(arg
));
3810 } else if (!strcasecmp(cmd
, "RTSPBindAddress")) {
3811 get_arg(arg
, sizeof(arg
), &p
);
3812 if (resolve_host(&my_rtsp_addr
.sin_addr
, arg
) != 0) {
3813 fprintf(stderr
, "%s:%d: Invalid host/IP address: %s\n",
3814 filename
, line_num
, arg
);
3817 } else if (!strcasecmp(cmd
, "MaxClients")) {
3818 get_arg(arg
, sizeof(arg
), &p
);
3820 if (val
< 1 || val
> HTTP_MAX_CONNECTIONS
) {
3821 fprintf(stderr
, "%s:%d: Invalid MaxClients: %s\n",
3822 filename
, line_num
, arg
);
3825 nb_max_connections
= val
;
3827 } else if (!strcasecmp(cmd
, "MaxBandwidth")) {
3829 get_arg(arg
, sizeof(arg
), &p
);
3831 if (llval
< 10 || llval
> 10000000) {
3832 fprintf(stderr
, "%s:%d: Invalid MaxBandwidth: %s\n",
3833 filename
, line_num
, arg
);
3836 max_bandwidth
= llval
;
3837 } else if (!strcasecmp(cmd
, "CustomLog")) {
3838 if (!ffserver_debug
)
3839 get_arg(logfilename
, sizeof(logfilename
), &p
);
3840 } else if (!strcasecmp(cmd
, "<Feed")) {
3841 /*********************************************/
3842 /* Feed related options */
3844 if (stream
|| feed
) {
3845 fprintf(stderr
, "%s:%d: Already in a tag\n",
3846 filename
, line_num
);
3848 feed
= av_mallocz(sizeof(FFStream
));
3849 /* add in stream list */
3850 *last_stream
= feed
;
3851 last_stream
= &feed
->next
;
3852 /* add in feed list */
3854 last_feed
= &feed
->next_feed
;
3856 get_arg(feed
->filename
, sizeof(feed
->filename
), &p
);
3857 q
= strrchr(feed
->filename
, '>');
3860 feed
->fmt
= guess_format("ffm", NULL
, NULL
);
3861 /* defaut feed file */
3862 snprintf(feed
->feed_filename
, sizeof(feed
->feed_filename
),
3863 "/tmp/%s.ffm", feed
->filename
);
3864 feed
->feed_max_size
= 5 * 1024 * 1024;
3866 feed
->feed
= feed
; /* self feeding :-) */
3868 } else if (!strcasecmp(cmd
, "Launch")) {
3872 feed
->child_argv
= av_mallocz(64 * sizeof(char *));
3874 for (i
= 0; i
< 62; i
++) {
3875 get_arg(arg
, sizeof(arg
), &p
);
3879 feed
->child_argv
[i
] = av_strdup(arg
);
3882 feed
->child_argv
[i
] = av_malloc(30 + strlen(feed
->filename
));
3884 snprintf(feed
->child_argv
[i
], 30+strlen(feed
->filename
),
3886 (my_http_addr
.sin_addr
.s_addr
== INADDR_ANY
) ? "127.0.0.1" :
3887 inet_ntoa(my_http_addr
.sin_addr
),
3888 ntohs(my_http_addr
.sin_port
), feed
->filename
);
3890 } else if (!strcasecmp(cmd
, "ReadOnlyFile")) {
3892 get_arg(feed
->feed_filename
, sizeof(feed
->feed_filename
), &p
);
3894 } else if (stream
) {
3895 get_arg(stream
->feed_filename
, sizeof(stream
->feed_filename
), &p
);
3897 } else if (!strcasecmp(cmd
, "File")) {
3899 get_arg(feed
->feed_filename
, sizeof(feed
->feed_filename
), &p
);
3901 get_arg(stream
->feed_filename
, sizeof(stream
->feed_filename
), &p
);
3902 } else if (!strcasecmp(cmd
, "FileMaxSize")) {
3907 get_arg(arg
, sizeof(arg
), &p
);
3909 fsize
= strtod(p1
, &p1
);
3910 switch(toupper(*p1
)) {
3915 fsize
*= 1024 * 1024;
3918 fsize
*= 1024 * 1024 * 1024;
3921 feed
->feed_max_size
= (int64_t)fsize
;
3923 } else if (!strcasecmp(cmd
, "</Feed>")) {
3925 fprintf(stderr
, "%s:%d: No corresponding <Feed> for </Feed>\n",
3926 filename
, line_num
);
3930 } else if (!strcasecmp(cmd
, "<Stream")) {
3931 /*********************************************/
3932 /* Stream related options */
3934 if (stream
|| feed
) {
3935 fprintf(stderr
, "%s:%d: Already in a tag\n",
3936 filename
, line_num
);
3938 const AVClass
*class;
3939 stream
= av_mallocz(sizeof(FFStream
));
3940 *last_stream
= stream
;
3941 last_stream
= &stream
->next
;
3943 get_arg(stream
->filename
, sizeof(stream
->filename
), &p
);
3944 q
= strrchr(stream
->filename
, '>');
3947 stream
->fmt
= guess_stream_format(NULL
, stream
->filename
, NULL
);
3948 /* fetch avclass so AVOption works
3949 * FIXME try to use avcodec_get_context_defaults2
3950 * without changing defaults too much */
3951 avcodec_get_context_defaults(&video_enc
);
3952 class = video_enc
.av_class
;
3953 memset(&audio_enc
, 0, sizeof(AVCodecContext
));
3954 memset(&video_enc
, 0, sizeof(AVCodecContext
));
3955 audio_enc
.av_class
= class;
3956 video_enc
.av_class
= class;
3957 audio_id
= CODEC_ID_NONE
;
3958 video_id
= CODEC_ID_NONE
;
3960 audio_id
= stream
->fmt
->audio_codec
;
3961 video_id
= stream
->fmt
->video_codec
;
3964 } else if (!strcasecmp(cmd
, "Feed")) {
3965 get_arg(arg
, sizeof(arg
), &p
);
3970 while (sfeed
!= NULL
) {
3971 if (!strcmp(sfeed
->filename
, arg
))
3973 sfeed
= sfeed
->next_feed
;
3976 fprintf(stderr
, "%s:%d: feed '%s' not defined\n",
3977 filename
, line_num
, arg
);
3979 stream
->feed
= sfeed
;
3981 } else if (!strcasecmp(cmd
, "Format")) {
3982 get_arg(arg
, sizeof(arg
), &p
);
3984 if (!strcmp(arg
, "status")) {
3985 stream
->stream_type
= STREAM_TYPE_STATUS
;
3988 stream
->stream_type
= STREAM_TYPE_LIVE
;
3989 /* jpeg cannot be used here, so use single frame jpeg */
3990 if (!strcmp(arg
, "jpeg"))
3991 strcpy(arg
, "mjpeg");
3992 stream
->fmt
= guess_stream_format(arg
, NULL
, NULL
);
3994 fprintf(stderr
, "%s:%d: Unknown Format: %s\n",
3995 filename
, line_num
, arg
);
4000 audio_id
= stream
->fmt
->audio_codec
;
4001 video_id
= stream
->fmt
->video_codec
;
4004 } else if (!strcasecmp(cmd
, "InputFormat")) {
4005 get_arg(arg
, sizeof(arg
), &p
);
4006 stream
->ifmt
= av_find_input_format(arg
);
4007 if (!stream
->ifmt
) {
4008 fprintf(stderr
, "%s:%d: Unknown input format: %s\n",
4009 filename
, line_num
, arg
);
4011 } else if (!strcasecmp(cmd
, "FaviconURL")) {
4012 if (stream
&& stream
->stream_type
== STREAM_TYPE_STATUS
) {
4013 get_arg(stream
->feed_filename
, sizeof(stream
->feed_filename
), &p
);
4015 fprintf(stderr
, "%s:%d: FaviconURL only permitted for status streams\n",
4016 filename
, line_num
);
4019 } else if (!strcasecmp(cmd
, "Author")) {
4021 get_arg(stream
->author
, sizeof(stream
->author
), &p
);
4022 } else if (!strcasecmp(cmd
, "Comment")) {
4024 get_arg(stream
->comment
, sizeof(stream
->comment
), &p
);
4025 } else if (!strcasecmp(cmd
, "Copyright")) {
4027 get_arg(stream
->copyright
, sizeof(stream
->copyright
), &p
);
4028 } else if (!strcasecmp(cmd
, "Title")) {
4030 get_arg(stream
->title
, sizeof(stream
->title
), &p
);
4031 } else if (!strcasecmp(cmd
, "Preroll")) {
4032 get_arg(arg
, sizeof(arg
), &p
);
4034 stream
->prebuffer
= atof(arg
) * 1000;
4035 } else if (!strcasecmp(cmd
, "StartSendOnKey")) {
4037 stream
->send_on_key
= 1;
4038 } else if (!strcasecmp(cmd
, "AudioCodec")) {
4039 get_arg(arg
, sizeof(arg
), &p
);
4040 audio_id
= opt_audio_codec(arg
);
4041 if (audio_id
== CODEC_ID_NONE
) {
4042 fprintf(stderr
, "%s:%d: Unknown AudioCodec: %s\n",
4043 filename
, line_num
, arg
);
4046 } else if (!strcasecmp(cmd
, "VideoCodec")) {
4047 get_arg(arg
, sizeof(arg
), &p
);
4048 video_id
= opt_video_codec(arg
);
4049 if (video_id
== CODEC_ID_NONE
) {
4050 fprintf(stderr
, "%s:%d: Unknown VideoCodec: %s\n",
4051 filename
, line_num
, arg
);
4054 } else if (!strcasecmp(cmd
, "MaxTime")) {
4055 get_arg(arg
, sizeof(arg
), &p
);
4057 stream
->max_time
= atof(arg
) * 1000;
4058 } else if (!strcasecmp(cmd
, "AudioBitRate")) {
4059 get_arg(arg
, sizeof(arg
), &p
);
4061 audio_enc
.bit_rate
= atoi(arg
) * 1000;
4062 } else if (!strcasecmp(cmd
, "AudioChannels")) {
4063 get_arg(arg
, sizeof(arg
), &p
);
4065 audio_enc
.channels
= atoi(arg
);
4066 } else if (!strcasecmp(cmd
, "AudioSampleRate")) {
4067 get_arg(arg
, sizeof(arg
), &p
);
4069 audio_enc
.sample_rate
= atoi(arg
);
4070 } else if (!strcasecmp(cmd
, "AudioQuality")) {
4071 get_arg(arg
, sizeof(arg
), &p
);
4073 // audio_enc.quality = atof(arg) * 1000;
4075 } else if (!strcasecmp(cmd
, "VideoBitRateRange")) {
4077 int minrate
, maxrate
;
4079 get_arg(arg
, sizeof(arg
), &p
);
4081 if (sscanf(arg
, "%d-%d", &minrate
, &maxrate
) == 2) {
4082 video_enc
.rc_min_rate
= minrate
* 1000;
4083 video_enc
.rc_max_rate
= maxrate
* 1000;
4085 fprintf(stderr
, "%s:%d: Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n",
4086 filename
, line_num
, arg
);
4090 } else if (!strcasecmp(cmd
, "Debug")) {
4092 get_arg(arg
, sizeof(arg
), &p
);
4093 video_enc
.debug
= strtol(arg
,0,0);
4095 } else if (!strcasecmp(cmd
, "Strict")) {
4097 get_arg(arg
, sizeof(arg
), &p
);
4098 video_enc
.strict_std_compliance
= atoi(arg
);
4100 } else if (!strcasecmp(cmd
, "VideoBufferSize")) {
4102 get_arg(arg
, sizeof(arg
), &p
);
4103 video_enc
.rc_buffer_size
= atoi(arg
) * 8*1024;
4105 } else if (!strcasecmp(cmd
, "VideoBitRateTolerance")) {
4107 get_arg(arg
, sizeof(arg
), &p
);
4108 video_enc
.bit_rate_tolerance
= atoi(arg
) * 1000;
4110 } else if (!strcasecmp(cmd
, "VideoBitRate")) {
4111 get_arg(arg
, sizeof(arg
), &p
);
4113 video_enc
.bit_rate
= atoi(arg
) * 1000;
4115 } else if (!strcasecmp(cmd
, "VideoSize")) {
4116 get_arg(arg
, sizeof(arg
), &p
);
4118 av_parse_video_frame_size(&video_enc
.width
, &video_enc
.height
, arg
);
4119 if ((video_enc
.width
% 16) != 0 ||
4120 (video_enc
.height
% 16) != 0) {
4121 fprintf(stderr
, "%s:%d: Image size must be a multiple of 16\n",
4122 filename
, line_num
);
4126 } else if (!strcasecmp(cmd
, "VideoFrameRate")) {
4127 get_arg(arg
, sizeof(arg
), &p
);
4129 AVRational frame_rate
;
4130 if (av_parse_video_frame_rate(&frame_rate
, arg
) < 0) {
4131 fprintf(stderr
, "Incorrect frame rate\n");
4134 video_enc
.time_base
.num
= frame_rate
.den
;
4135 video_enc
.time_base
.den
= frame_rate
.num
;
4138 } else if (!strcasecmp(cmd
, "VideoGopSize")) {
4139 get_arg(arg
, sizeof(arg
), &p
);
4141 video_enc
.gop_size
= atoi(arg
);
4142 } else if (!strcasecmp(cmd
, "VideoIntraOnly")) {
4144 video_enc
.gop_size
= 1;
4145 } else if (!strcasecmp(cmd
, "VideoHighQuality")) {
4147 video_enc
.mb_decision
= FF_MB_DECISION_BITS
;
4148 } else if (!strcasecmp(cmd
, "Video4MotionVector")) {
4150 video_enc
.mb_decision
= FF_MB_DECISION_BITS
; //FIXME remove
4151 video_enc
.flags
|= CODEC_FLAG_4MV
;
4153 } else if (!strcasecmp(cmd
, "AVOptionVideo") ||
4154 !strcasecmp(cmd
, "AVOptionAudio")) {
4156 AVCodecContext
*avctx
;
4158 get_arg(arg
, sizeof(arg
), &p
);
4159 get_arg(arg2
, sizeof(arg2
), &p
);
4160 if (!strcasecmp(cmd
, "AVOptionVideo")) {
4162 type
= AV_OPT_FLAG_VIDEO_PARAM
;
4165 type
= AV_OPT_FLAG_AUDIO_PARAM
;
4167 if (opt_default(arg
, arg2
, avctx
, type
|AV_OPT_FLAG_ENCODING_PARAM
)) {
4168 fprintf(stderr
, "AVOption error: %s %s\n", arg
, arg2
);
4171 } else if (!strcasecmp(cmd
, "VideoTag")) {
4172 get_arg(arg
, sizeof(arg
), &p
);
4173 if ((strlen(arg
) == 4) && stream
)
4174 video_enc
.codec_tag
= ff_get_fourcc(arg
);
4175 } else if (!strcasecmp(cmd
, "BitExact")) {
4177 video_enc
.flags
|= CODEC_FLAG_BITEXACT
;
4178 } else if (!strcasecmp(cmd
, "DctFastint")) {
4180 video_enc
.dct_algo
= FF_DCT_FASTINT
;
4181 } else if (!strcasecmp(cmd
, "IdctSimple")) {
4183 video_enc
.idct_algo
= FF_IDCT_SIMPLE
;
4184 } else if (!strcasecmp(cmd
, "Qscale")) {
4185 get_arg(arg
, sizeof(arg
), &p
);
4187 video_enc
.flags
|= CODEC_FLAG_QSCALE
;
4188 video_enc
.global_quality
= FF_QP2LAMBDA
* atoi(arg
);
4190 } else if (!strcasecmp(cmd
, "VideoQDiff")) {
4191 get_arg(arg
, sizeof(arg
), &p
);
4193 video_enc
.max_qdiff
= atoi(arg
);
4194 if (video_enc
.max_qdiff
< 1 || video_enc
.max_qdiff
> 31) {
4195 fprintf(stderr
, "%s:%d: VideoQDiff out of range\n",
4196 filename
, line_num
);
4200 } else if (!strcasecmp(cmd
, "VideoQMax")) {
4201 get_arg(arg
, sizeof(arg
), &p
);
4203 video_enc
.qmax
= atoi(arg
);
4204 if (video_enc
.qmax
< 1 || video_enc
.qmax
> 31) {
4205 fprintf(stderr
, "%s:%d: VideoQMax out of range\n",
4206 filename
, line_num
);
4210 } else if (!strcasecmp(cmd
, "VideoQMin")) {
4211 get_arg(arg
, sizeof(arg
), &p
);
4213 video_enc
.qmin
= atoi(arg
);
4214 if (video_enc
.qmin
< 1 || video_enc
.qmin
> 31) {
4215 fprintf(stderr
, "%s:%d: VideoQMin out of range\n",
4216 filename
, line_num
);
4220 } else if (!strcasecmp(cmd
, "LumaElim")) {
4221 get_arg(arg
, sizeof(arg
), &p
);
4223 video_enc
.luma_elim_threshold
= atoi(arg
);
4224 } else if (!strcasecmp(cmd
, "ChromaElim")) {
4225 get_arg(arg
, sizeof(arg
), &p
);
4227 video_enc
.chroma_elim_threshold
= atoi(arg
);
4228 } else if (!strcasecmp(cmd
, "LumiMask")) {
4229 get_arg(arg
, sizeof(arg
), &p
);
4231 video_enc
.lumi_masking
= atof(arg
);
4232 } else if (!strcasecmp(cmd
, "DarkMask")) {
4233 get_arg(arg
, sizeof(arg
), &p
);
4235 video_enc
.dark_masking
= atof(arg
);
4236 } else if (!strcasecmp(cmd
, "NoVideo")) {
4237 video_id
= CODEC_ID_NONE
;
4238 } else if (!strcasecmp(cmd
, "NoAudio")) {
4239 audio_id
= CODEC_ID_NONE
;
4240 } else if (!strcasecmp(cmd
, "ACL")) {
4243 get_arg(arg
, sizeof(arg
), &p
);
4244 if (strcasecmp(arg
, "allow") == 0)
4245 acl
.action
= IP_ALLOW
;
4246 else if (strcasecmp(arg
, "deny") == 0)
4247 acl
.action
= IP_DENY
;
4249 fprintf(stderr
, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
4250 filename
, line_num
, arg
);
4254 get_arg(arg
, sizeof(arg
), &p
);
4256 if (resolve_host(&acl
.first
, arg
) != 0) {
4257 fprintf(stderr
, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4258 filename
, line_num
, arg
);
4261 acl
.last
= acl
.first
;
4263 get_arg(arg
, sizeof(arg
), &p
);
4266 if (resolve_host(&acl
.last
, arg
) != 0) {
4267 fprintf(stderr
, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4268 filename
, line_num
, arg
);
4274 IPAddressACL
*nacl
= av_mallocz(sizeof(*nacl
));
4275 IPAddressACL
**naclp
= 0;
4281 naclp
= &stream
->acl
;
4285 fprintf(stderr
, "%s:%d: ACL found not in <stream> or <feed>\n",
4286 filename
, line_num
);
4292 naclp
= &(*naclp
)->next
;
4297 } else if (!strcasecmp(cmd
, "RTSPOption")) {
4298 get_arg(arg
, sizeof(arg
), &p
);
4300 av_freep(&stream
->rtsp_option
);
4301 stream
->rtsp_option
= av_strdup(arg
);
4303 } else if (!strcasecmp(cmd
, "MulticastAddress")) {
4304 get_arg(arg
, sizeof(arg
), &p
);
4306 if (resolve_host(&stream
->multicast_ip
, arg
) != 0) {
4307 fprintf(stderr
, "%s:%d: Invalid host/IP address: %s\n",
4308 filename
, line_num
, arg
);
4311 stream
->is_multicast
= 1;
4312 stream
->loop
= 1; /* default is looping */
4314 } else if (!strcasecmp(cmd
, "MulticastPort")) {
4315 get_arg(arg
, sizeof(arg
), &p
);
4317 stream
->multicast_port
= atoi(arg
);
4318 } else if (!strcasecmp(cmd
, "MulticastTTL")) {
4319 get_arg(arg
, sizeof(arg
), &p
);
4321 stream
->multicast_ttl
= atoi(arg
);
4322 } else if (!strcasecmp(cmd
, "NoLoop")) {
4325 } else if (!strcasecmp(cmd
, "</Stream>")) {
4327 fprintf(stderr
, "%s:%d: No corresponding <Stream> for </Stream>\n",
4328 filename
, line_num
);
4331 if (stream
->feed
&& stream
->fmt
&& strcmp(stream
->fmt
->name
, "ffm") != 0) {
4332 if (audio_id
!= CODEC_ID_NONE
) {
4333 audio_enc
.codec_type
= CODEC_TYPE_AUDIO
;
4334 audio_enc
.codec_id
= audio_id
;
4335 add_codec(stream
, &audio_enc
);
4337 if (video_id
!= CODEC_ID_NONE
) {
4338 video_enc
.codec_type
= CODEC_TYPE_VIDEO
;
4339 video_enc
.codec_id
= video_id
;
4340 add_codec(stream
, &video_enc
);
4345 } else if (!strcasecmp(cmd
, "<Redirect")) {
4346 /*********************************************/
4348 if (stream
|| feed
|| redirect
) {
4349 fprintf(stderr
, "%s:%d: Already in a tag\n",
4350 filename
, line_num
);
4353 redirect
= av_mallocz(sizeof(FFStream
));
4354 *last_stream
= redirect
;
4355 last_stream
= &redirect
->next
;
4357 get_arg(redirect
->filename
, sizeof(redirect
->filename
), &p
);
4358 q
= strrchr(redirect
->filename
, '>');
4361 redirect
->stream_type
= STREAM_TYPE_REDIRECT
;
4363 } else if (!strcasecmp(cmd
, "URL")) {
4365 get_arg(redirect
->feed_filename
, sizeof(redirect
->feed_filename
), &p
);
4366 } else if (!strcasecmp(cmd
, "</Redirect>")) {
4368 fprintf(stderr
, "%s:%d: No corresponding <Redirect> for </Redirect>\n",
4369 filename
, line_num
);
4372 if (!redirect
->feed_filename
[0]) {
4373 fprintf(stderr
, "%s:%d: No URL found for <Redirect>\n",
4374 filename
, line_num
);
4379 } else if (!strcasecmp(cmd
, "LoadModule")) {
4380 get_arg(arg
, sizeof(arg
), &p
);
4384 fprintf(stderr
, "%s:%d: Module support not compiled into this version: '%s'\n",
4385 filename
, line_num
, arg
);
4389 fprintf(stderr
, "%s:%d: Incorrect keyword: '%s'\n",
4390 filename
, line_num
, cmd
);
4402 static void handle_child_exit(int sig
)
4407 while ((pid
= waitpid(-1, &status
, WNOHANG
)) > 0) {
4410 for (feed
= first_feed
; feed
; feed
= feed
->next
) {
4411 if (feed
->pid
== pid
) {
4412 int uptime
= time(0) - feed
->pid_start
;
4415 fprintf(stderr
, "%s: Pid %d exited with status %d after %d seconds\n", feed
->filename
, pid
, status
, uptime
);
4418 /* Turn off any more restarts */
4419 feed
->child_argv
= 0;
4424 need_to_start_children
= 1;
4427 static void opt_debug()
4430 ffserver_daemon
= 0;
4431 logfilename
[0] = '-';
4434 static void opt_show_help(void)
4436 printf("usage: ffserver [options]\n"
4437 "Hyper fast multi format Audio/Video streaming server\n");
4439 show_help_options(options
, "Main options:\n", 0, 0);
4442 static const OptionDef options
[] = {
4443 { "h", OPT_EXIT
, {(void*)opt_show_help
}, "show help" },
4444 { "version", OPT_EXIT
, {(void*)show_version
}, "show version" },
4445 { "L", OPT_EXIT
, {(void*)show_license
}, "show license" },
4446 { "formats", OPT_EXIT
, {(void*)show_formats
}, "show available formats, codecs, protocols, ..." },
4447 { "n", OPT_BOOL
, {(void *)&no_launch
}, "enable no-launch mode" },
4448 { "d", 0, {(void*)opt_debug
}, "enable debug mode" },
4449 { "f", HAS_ARG
| OPT_STRING
, {(void*)&config_filename
}, "use configfile instead of /etc/ffserver.conf", "configfile" },
4453 int main(int argc
, char **argv
)
4455 struct sigaction sigact
;
4461 config_filename
= "/etc/ffserver.conf";
4463 my_program_name
= argv
[0];
4464 my_program_dir
= getcwd(0, 0);
4465 ffserver_daemon
= 1;
4467 parse_options(argc
, argv
, options
, NULL
);
4469 unsetenv("http_proxy"); /* Kill the http_proxy */
4471 av_init_random(av_gettime() + (getpid() << 16), &random_state
);
4473 memset(&sigact
, 0, sizeof(sigact
));
4474 sigact
.sa_handler
= handle_child_exit
;
4475 sigact
.sa_flags
= SA_NOCLDSTOP
| SA_RESTART
;
4476 sigaction(SIGCHLD
, &sigact
, 0);
4478 if (parse_ffconfig(config_filename
) < 0) {
4479 fprintf(stderr
, "Incorrect config file - exiting.\n");
4483 /* open log file if needed */
4484 if (logfilename
[0] != '\0') {
4485 if (!strcmp(logfilename
, "-"))
4488 logfile
= fopen(logfilename
, "a");
4489 av_log_set_callback(http_av_log
);
4492 build_file_streams();
4494 build_feed_streams();
4496 compute_bandwidth();
4498 /* put the process in background and detach it from its TTY */
4499 if (ffserver_daemon
) {
4506 } else if (pid
> 0) {
4513 open("/dev/null", O_RDWR
);
4514 if (strcmp(logfilename
, "-") != 0) {
4524 signal(SIGPIPE
, SIG_IGN
);
4526 if (ffserver_daemon
)
4529 if (http_server() < 0) {
4530 http_log("Could not start server\n");