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 #if HAVE_CLOSESOCKET != 1
24 #define closesocket close
33 #include <sys/ioctl.h>
34 #ifdef HAVE_SYS_POLL_H
39 #undef time //needed because HAVE_AV_CONFIG_H is defined on top
55 /* maximum number of simultaneous HTTP connections */
56 #define HTTP_MAX_CONNECTIONS 2000
59 HTTPSTATE_WAIT_REQUEST
,
60 HTTPSTATE_SEND_HEADER
,
61 HTTPSTATE_SEND_DATA_HEADER
,
62 HTTPSTATE_SEND_DATA
, /* sending TCP or UDP data */
63 HTTPSTATE_SEND_DATA_TRAILER
,
64 HTTPSTATE_RECEIVE_DATA
,
65 HTTPSTATE_WAIT_FEED
, /* wait for data from the feed */
68 RTSPSTATE_WAIT_REQUEST
,
70 RTSPSTATE_SEND_PACKET
,
73 const char *http_state
[] = {
89 #define IOBUFFER_INIT_SIZE 8192
91 /* timeouts are in ms */
92 #define HTTP_REQUEST_TIMEOUT (15 * 1000)
93 #define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
95 #define SYNC_TIMEOUT (10 * 1000)
98 int64_t count1
, count2
;
102 /* context associated with one connection */
103 typedef struct HTTPContext
{
104 enum HTTPState state
;
105 int fd
; /* socket file descriptor */
106 struct sockaddr_in from_addr
; /* origin */
107 struct pollfd
*poll_entry
; /* used when polling */
109 uint8_t *buffer_ptr
, *buffer_end
;
112 struct HTTPContext
*next
;
113 int got_key_frame
; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
117 /* input format handling */
118 AVFormatContext
*fmt_in
;
119 int64_t start_time
; /* In milliseconds - this wraps fairly often */
120 int64_t first_pts
; /* initial pts value */
121 int64_t cur_pts
; /* current pts value from the stream in us */
122 int64_t cur_frame_duration
; /* duration of the current frame in us */
123 int cur_frame_bytes
; /* output frame size, needed to compute
124 the time at which we send each
126 int pts_stream_index
; /* stream we choose as clock reference */
127 int64_t cur_clock
; /* current clock reference value in us */
128 /* output format handling */
129 struct FFStream
*stream
;
130 /* -1 is invalid stream */
131 int feed_streams
[MAX_STREAMS
]; /* index of streams in the feed */
132 int switch_feed_streams
[MAX_STREAMS
]; /* index of streams in the feed */
134 AVFormatContext fmt_ctx
; /* instance of FFStream for one user */
135 int last_packet_sent
; /* true if last data packet was sent */
137 DataRateData datarate
;
144 int is_packetized
; /* if true, the stream is packetized */
145 int packet_stream_index
; /* current stream for output in state machine */
147 /* RTSP state specific */
148 uint8_t *pb_buffer
; /* XXX: use that in all the code */
150 int seq
; /* RTSP sequence number */
152 /* RTP state specific */
153 enum RTSPProtocol rtp_protocol
;
154 char session_id
[32]; /* session id */
155 AVFormatContext
*rtp_ctx
[MAX_STREAMS
];
157 /* RTP/UDP specific */
158 URLContext
*rtp_handles
[MAX_STREAMS
];
160 /* RTP/TCP specific */
161 struct HTTPContext
*rtsp_c
;
162 uint8_t *packet_buffer
, *packet_buffer_ptr
, *packet_buffer_end
;
165 static AVFrame dummy_frame
;
167 /* each generated stream is described here */
171 STREAM_TYPE_REDIRECT
,
174 enum IPAddressAction
{
179 typedef struct IPAddressACL
{
180 struct IPAddressACL
*next
;
181 enum IPAddressAction action
;
182 /* These are in host order */
183 struct in_addr first
;
187 /* description of each stream of the ffserver.conf file */
188 typedef struct FFStream
{
189 enum StreamType stream_type
;
190 char filename
[1024]; /* stream filename */
191 struct FFStream
*feed
; /* feed we are using (can be null if
193 AVFormatParameters
*ap_in
; /* input parameters */
194 AVInputFormat
*ifmt
; /* if non NULL, force input format */
198 int prebuffer
; /* Number of millseconds early to start */
199 int64_t max_time
; /* Number of milliseconds to run */
201 AVStream
*streams
[MAX_STREAMS
];
202 int feed_streams
[MAX_STREAMS
]; /* index of streams in the feed */
203 char feed_filename
[1024]; /* file name of the feed storage, or
204 input file name for a stream */
209 pid_t pid
; /* Of ffmpeg process */
210 time_t pid_start
; /* Of ffmpeg process */
212 struct FFStream
*next
;
213 int bandwidth
; /* bandwidth, in kbits/s */
216 /* multicast specific */
218 struct in_addr multicast_ip
;
219 int multicast_port
; /* first port used for multicast */
221 int loop
; /* if true, send the stream in loops (only meaningful if file) */
224 int feed_opened
; /* true if someone is writing to the feed */
225 int is_feed
; /* true if it is a feed */
226 int readonly
; /* True if writing is prohibited to the file */
228 int64_t bytes_served
;
229 int64_t feed_max_size
; /* maximum storage size, zero means unlimited */
230 int64_t feed_write_index
; /* current write position in feed (it wraps round) */
231 int64_t feed_size
; /* current size of feed */
232 struct FFStream
*next_feed
;
235 typedef struct FeedData
{
236 long long data_count
;
237 float avg_frame_size
; /* frame size averraged over last frames with exponential mean */
240 static struct sockaddr_in my_http_addr
;
241 static struct sockaddr_in my_rtsp_addr
;
243 static char logfilename
[1024];
244 static HTTPContext
*first_http_ctx
;
245 static FFStream
*first_feed
; /* contains only feeds */
246 static FFStream
*first_stream
; /* contains all streams, including feeds */
248 static void new_connection(int server_fd
, int is_rtsp
);
249 static void close_connection(HTTPContext
*c
);
252 static int handle_connection(HTTPContext
*c
);
253 static int http_parse_request(HTTPContext
*c
);
254 static int http_send_data(HTTPContext
*c
);
255 static void compute_stats(HTTPContext
*c
);
256 static int open_input_stream(HTTPContext
*c
, const char *info
);
257 static int http_start_receive_data(HTTPContext
*c
);
258 static int http_receive_data(HTTPContext
*c
);
261 static int rtsp_parse_request(HTTPContext
*c
);
262 static void rtsp_cmd_describe(HTTPContext
*c
, const char *url
);
263 static void rtsp_cmd_options(HTTPContext
*c
, const char *url
);
264 static void rtsp_cmd_setup(HTTPContext
*c
, const char *url
, RTSPHeader
*h
);
265 static void rtsp_cmd_play(HTTPContext
*c
, const char *url
, RTSPHeader
*h
);
266 static void rtsp_cmd_pause(HTTPContext
*c
, const char *url
, RTSPHeader
*h
);
267 static void rtsp_cmd_teardown(HTTPContext
*c
, const char *url
, RTSPHeader
*h
);
270 static int prepare_sdp_description(FFStream
*stream
, uint8_t **pbuffer
,
271 struct in_addr my_ip
);
274 static HTTPContext
*rtp_new_connection(struct sockaddr_in
*from_addr
,
275 FFStream
*stream
, const char *session_id
,
276 enum RTSPProtocol rtp_protocol
);
277 static int rtp_new_av_stream(HTTPContext
*c
,
278 int stream_index
, struct sockaddr_in
*dest_addr
,
279 HTTPContext
*rtsp_c
);
281 static const char *my_program_name
;
282 static const char *my_program_dir
;
284 static int ffserver_debug
;
285 static int ffserver_daemon
;
286 static int no_launch
;
287 static int need_to_start_children
;
289 static int nb_max_connections
;
290 static int nb_connections
;
292 static int max_bandwidth
;
293 static int current_bandwidth
;
295 static int64_t cur_time
; // Making this global saves on passing it around everywhere
297 static AVRandomState random_state
;
299 static FILE *logfile
= NULL
;
301 static void __attribute__ ((format (printf
, 1, 2))) http_log(const char *fmt
, ...)
307 vfprintf(logfile
, fmt
, ap
);
313 static char *ctime1(char *buf2
)
321 p
= buf2
+ strlen(p
) - 1;
327 static void log_connection(HTTPContext
*c
)
334 http_log("%s - - [%s] \"%s %s %s\" %d %"PRId64
"\n",
335 inet_ntoa(c
->from_addr
.sin_addr
),
336 ctime1(buf2
), c
->method
, c
->url
,
337 c
->protocol
, (c
->http_error
? c
->http_error
: 200), c
->data_count
);
340 static void update_datarate(DataRateData
*drd
, int64_t count
)
342 if (!drd
->time1
&& !drd
->count1
) {
343 drd
->time1
= drd
->time2
= cur_time
;
344 drd
->count1
= drd
->count2
= count
;
346 if (cur_time
- drd
->time2
> 5000) {
347 drd
->time1
= drd
->time2
;
348 drd
->count1
= drd
->count2
;
349 drd
->time2
= cur_time
;
355 /* In bytes per second */
356 static int compute_datarate(DataRateData
*drd
, int64_t count
)
358 if (cur_time
== drd
->time1
)
361 return ((count
- drd
->count1
) * 1000) / (cur_time
- drd
->time1
);
365 static void start_children(FFStream
*feed
)
370 for (; feed
; feed
= feed
->next
) {
371 if (feed
->child_argv
&& !feed
->pid
) {
372 feed
->pid_start
= time(0);
377 fprintf(stderr
, "Unable to create children\n");
386 for (i
= 3; i
< 256; i
++) {
390 if (!ffserver_debug
) {
391 i
= open("/dev/null", O_RDWR
);
400 av_strlcpy(pathname
, my_program_name
, sizeof(pathname
));
402 slash
= strrchr(pathname
, '/');
408 strcpy(slash
, "ffmpeg");
410 /* This is needed to make relative pathnames work */
411 chdir(my_program_dir
);
413 signal(SIGPIPE
, SIG_DFL
);
415 execvp(pathname
, feed
->child_argv
);
423 /* open a listening socket */
424 static int socket_open_listen(struct sockaddr_in
*my_addr
)
428 server_fd
= socket(AF_INET
,SOCK_STREAM
,0);
435 setsockopt(server_fd
, SOL_SOCKET
, SO_REUSEADDR
, &tmp
, sizeof(tmp
));
437 if (bind (server_fd
, (struct sockaddr
*) my_addr
, sizeof (*my_addr
)) < 0) {
439 snprintf(bindmsg
, sizeof(bindmsg
), "bind(port %d)", ntohs(my_addr
->sin_port
));
441 closesocket(server_fd
);
445 if (listen (server_fd
, 5) < 0) {
447 closesocket(server_fd
);
450 ff_socket_nonblock(server_fd
, 1);
455 /* start all multicast streams */
456 static void start_multicast(void)
461 struct sockaddr_in dest_addr
;
462 int default_port
, stream_index
;
465 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
466 if (stream
->is_multicast
) {
467 /* open the RTP connection */
468 snprintf(session_id
, sizeof(session_id
), "%08x%08x",
469 av_random(&random_state
), av_random(&random_state
));
471 /* choose a port if none given */
472 if (stream
->multicast_port
== 0) {
473 stream
->multicast_port
= default_port
;
477 dest_addr
.sin_family
= AF_INET
;
478 dest_addr
.sin_addr
= stream
->multicast_ip
;
479 dest_addr
.sin_port
= htons(stream
->multicast_port
);
481 rtp_c
= rtp_new_connection(&dest_addr
, stream
, session_id
,
482 RTSP_PROTOCOL_RTP_UDP_MULTICAST
);
486 if (open_input_stream(rtp_c
, "") < 0) {
487 fprintf(stderr
, "Could not open input stream for stream '%s'\n",
492 /* open each RTP stream */
493 for(stream_index
= 0; stream_index
< stream
->nb_streams
;
495 dest_addr
.sin_port
= htons(stream
->multicast_port
+
497 if (rtp_new_av_stream(rtp_c
, stream_index
, &dest_addr
, NULL
) < 0) {
498 fprintf(stderr
, "Could not open output stream '%s/streamid=%d'\n",
499 stream
->filename
, stream_index
);
504 /* change state to send data */
505 rtp_c
->state
= HTTPSTATE_SEND_DATA
;
510 /* main loop of the http server */
511 static int http_server(void)
513 int server_fd
, ret
, rtsp_server_fd
, delay
, delay1
;
514 struct pollfd poll_table
[HTTP_MAX_CONNECTIONS
+ 2], *poll_entry
;
515 HTTPContext
*c
, *c_next
;
517 server_fd
= socket_open_listen(&my_http_addr
);
521 rtsp_server_fd
= socket_open_listen(&my_rtsp_addr
);
522 if (rtsp_server_fd
< 0)
525 http_log("ffserver started.\n");
527 start_children(first_feed
);
529 first_http_ctx
= NULL
;
535 poll_entry
= poll_table
;
536 poll_entry
->fd
= server_fd
;
537 poll_entry
->events
= POLLIN
;
540 poll_entry
->fd
= rtsp_server_fd
;
541 poll_entry
->events
= POLLIN
;
544 /* wait for events on each HTTP handle */
551 case HTTPSTATE_SEND_HEADER
:
552 case RTSPSTATE_SEND_REPLY
:
553 case RTSPSTATE_SEND_PACKET
:
554 c
->poll_entry
= poll_entry
;
556 poll_entry
->events
= POLLOUT
;
559 case HTTPSTATE_SEND_DATA_HEADER
:
560 case HTTPSTATE_SEND_DATA
:
561 case HTTPSTATE_SEND_DATA_TRAILER
:
562 if (!c
->is_packetized
) {
563 /* for TCP, we output as much as we can (may need to put a limit) */
564 c
->poll_entry
= poll_entry
;
566 poll_entry
->events
= POLLOUT
;
569 /* when ffserver is doing the timing, we work by
570 looking at which packet need to be sent every
572 delay1
= 10; /* one tick wait XXX: 10 ms assumed */
577 case HTTPSTATE_WAIT_REQUEST
:
578 case HTTPSTATE_RECEIVE_DATA
:
579 case HTTPSTATE_WAIT_FEED
:
580 case RTSPSTATE_WAIT_REQUEST
:
581 /* need to catch errors */
582 c
->poll_entry
= poll_entry
;
584 poll_entry
->events
= POLLIN
;/* Maybe this will work */
588 c
->poll_entry
= NULL
;
594 /* wait for an event on one connection. We poll at least every
595 second to handle timeouts */
597 ret
= poll(poll_table
, poll_entry
- poll_table
, delay
);
598 if (ret
< 0 && ff_neterrno() != FF_NETERROR(EAGAIN
) &&
599 ff_neterrno() != FF_NETERROR(EINTR
))
603 cur_time
= av_gettime() / 1000;
605 if (need_to_start_children
) {
606 need_to_start_children
= 0;
607 start_children(first_feed
);
610 /* now handle the events */
611 for(c
= first_http_ctx
; c
!= NULL
; c
= c_next
) {
613 if (handle_connection(c
) < 0) {
614 /* close and free the connection */
620 poll_entry
= poll_table
;
621 /* new HTTP connection request ? */
622 if (poll_entry
->revents
& POLLIN
) {
623 new_connection(server_fd
, 0);
626 /* new RTSP connection request ? */
627 if (poll_entry
->revents
& POLLIN
) {
628 new_connection(rtsp_server_fd
, 1);
633 /* start waiting for a new HTTP/RTSP request */
634 static void start_wait_request(HTTPContext
*c
, int is_rtsp
)
636 c
->buffer_ptr
= c
->buffer
;
637 c
->buffer_end
= c
->buffer
+ c
->buffer_size
- 1; /* leave room for '\0' */
640 c
->timeout
= cur_time
+ RTSP_REQUEST_TIMEOUT
;
641 c
->state
= RTSPSTATE_WAIT_REQUEST
;
643 c
->timeout
= cur_time
+ HTTP_REQUEST_TIMEOUT
;
644 c
->state
= HTTPSTATE_WAIT_REQUEST
;
648 static void new_connection(int server_fd
, int is_rtsp
)
650 struct sockaddr_in from_addr
;
652 HTTPContext
*c
= NULL
;
654 len
= sizeof(from_addr
);
655 fd
= accept(server_fd
, (struct sockaddr
*)&from_addr
,
659 ff_socket_nonblock(fd
, 1);
661 /* XXX: should output a warning page when coming
662 close to the connection limit */
663 if (nb_connections
>= nb_max_connections
)
666 /* add a new connection */
667 c
= av_mallocz(sizeof(HTTPContext
));
672 c
->poll_entry
= NULL
;
673 c
->from_addr
= from_addr
;
674 c
->buffer_size
= IOBUFFER_INIT_SIZE
;
675 c
->buffer
= av_malloc(c
->buffer_size
);
679 c
->next
= first_http_ctx
;
683 start_wait_request(c
, is_rtsp
);
695 static void close_connection(HTTPContext
*c
)
697 HTTPContext
**cp
, *c1
;
699 AVFormatContext
*ctx
;
703 /* remove connection from list */
704 cp
= &first_http_ctx
;
705 while ((*cp
) != NULL
) {
714 /* remove references, if any (XXX: do it faster) */
715 for(c1
= first_http_ctx
; c1
!= NULL
; c1
= c1
->next
) {
720 /* remove connection associated resources */
724 /* close each frame parser */
725 for(i
=0;i
<c
->fmt_in
->nb_streams
;i
++) {
726 st
= c
->fmt_in
->streams
[i
];
727 if (st
->codec
->codec
) {
728 avcodec_close(st
->codec
);
731 av_close_input_file(c
->fmt_in
);
734 /* free RTP output streams if any */
737 nb_streams
= c
->stream
->nb_streams
;
739 for(i
=0;i
<nb_streams
;i
++) {
742 av_write_trailer(ctx
);
745 h
= c
->rtp_handles
[i
];
753 if (!c
->last_packet_sent
) {
756 if (url_open_dyn_buf(&ctx
->pb
) >= 0) {
757 av_write_trailer(ctx
);
758 url_close_dyn_buf(&ctx
->pb
, &c
->pb_buffer
);
763 for(i
=0; i
<ctx
->nb_streams
; i
++)
764 av_free(ctx
->streams
[i
]) ;
766 if (c
->stream
&& !c
->post
&& c
->stream
->stream_type
== STREAM_TYPE_LIVE
)
767 current_bandwidth
-= c
->stream
->bandwidth
;
769 /* signal that there is no feed if we are the feeder socket */
770 if (c
->state
== HTTPSTATE_RECEIVE_DATA
&& c
->stream
) {
771 c
->stream
->feed_opened
= 0;
775 av_freep(&c
->pb_buffer
);
776 av_freep(&c
->packet_buffer
);
782 static int handle_connection(HTTPContext
*c
)
787 case HTTPSTATE_WAIT_REQUEST
:
788 case RTSPSTATE_WAIT_REQUEST
:
790 if ((c
->timeout
- cur_time
) < 0)
792 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
))
795 /* no need to read if no events */
796 if (!(c
->poll_entry
->revents
& POLLIN
))
800 len
= recv(c
->fd
, c
->buffer_ptr
, 1, 0);
802 if (ff_neterrno() != FF_NETERROR(EAGAIN
) &&
803 ff_neterrno() != FF_NETERROR(EINTR
))
805 } else if (len
== 0) {
808 /* search for end of request. */
810 c
->buffer_ptr
+= len
;
812 if ((ptr
>= c
->buffer
+ 2 && !memcmp(ptr
-2, "\n\n", 2)) ||
813 (ptr
>= c
->buffer
+ 4 && !memcmp(ptr
-4, "\r\n\r\n", 4))) {
814 /* request found : parse it and reply */
815 if (c
->state
== HTTPSTATE_WAIT_REQUEST
) {
816 ret
= http_parse_request(c
);
818 ret
= rtsp_parse_request(c
);
822 } else if (ptr
>= c
->buffer_end
) {
823 /* request too long: cannot do anything */
825 } else goto read_loop
;
829 case HTTPSTATE_SEND_HEADER
:
830 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
))
833 /* no need to write if no events */
834 if (!(c
->poll_entry
->revents
& POLLOUT
))
836 len
= send(c
->fd
, c
->buffer_ptr
, c
->buffer_end
- c
->buffer_ptr
, 0);
838 if (ff_neterrno() != FF_NETERROR(EAGAIN
) &&
839 ff_neterrno() != FF_NETERROR(EINTR
)) {
840 /* error : close connection */
841 av_freep(&c
->pb_buffer
);
845 c
->buffer_ptr
+= len
;
847 c
->stream
->bytes_served
+= len
;
848 c
->data_count
+= len
;
849 if (c
->buffer_ptr
>= c
->buffer_end
) {
850 av_freep(&c
->pb_buffer
);
855 /* all the buffer was sent : synchronize to the incoming stream */
856 c
->state
= HTTPSTATE_SEND_DATA_HEADER
;
857 c
->buffer_ptr
= c
->buffer_end
= c
->buffer
;
862 case HTTPSTATE_SEND_DATA
:
863 case HTTPSTATE_SEND_DATA_HEADER
:
864 case HTTPSTATE_SEND_DATA_TRAILER
:
865 /* for packetized output, we consider we can always write (the
866 input streams sets the speed). It may be better to verify
867 that we do not rely too much on the kernel queues */
868 if (!c
->is_packetized
) {
869 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
))
872 /* no need to read if no events */
873 if (!(c
->poll_entry
->revents
& POLLOUT
))
876 if (http_send_data(c
) < 0)
878 /* close connection if trailer sent */
879 if (c
->state
== HTTPSTATE_SEND_DATA_TRAILER
)
882 case HTTPSTATE_RECEIVE_DATA
:
883 /* no need to read if no events */
884 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
))
886 if (!(c
->poll_entry
->revents
& POLLIN
))
888 if (http_receive_data(c
) < 0)
891 case HTTPSTATE_WAIT_FEED
:
892 /* no need to read if no events */
893 if (c
->poll_entry
->revents
& (POLLIN
| POLLERR
| POLLHUP
))
896 /* nothing to do, we'll be waken up by incoming feed packets */
899 case RTSPSTATE_SEND_REPLY
:
900 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
)) {
901 av_freep(&c
->pb_buffer
);
904 /* no need to write if no events */
905 if (!(c
->poll_entry
->revents
& POLLOUT
))
907 len
= send(c
->fd
, c
->buffer_ptr
, c
->buffer_end
- c
->buffer_ptr
, 0);
909 if (ff_neterrno() != FF_NETERROR(EAGAIN
) &&
910 ff_neterrno() != FF_NETERROR(EINTR
)) {
911 /* error : close connection */
912 av_freep(&c
->pb_buffer
);
916 c
->buffer_ptr
+= len
;
917 c
->data_count
+= len
;
918 if (c
->buffer_ptr
>= c
->buffer_end
) {
919 /* all the buffer was sent : wait for a new request */
920 av_freep(&c
->pb_buffer
);
921 start_wait_request(c
, 1);
925 case RTSPSTATE_SEND_PACKET
:
926 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
)) {
927 av_freep(&c
->packet_buffer
);
930 /* no need to write if no events */
931 if (!(c
->poll_entry
->revents
& POLLOUT
))
933 len
= send(c
->fd
, c
->packet_buffer_ptr
,
934 c
->packet_buffer_end
- c
->packet_buffer_ptr
, 0);
936 if (ff_neterrno() != FF_NETERROR(EAGAIN
) &&
937 ff_neterrno() != FF_NETERROR(EINTR
)) {
938 /* error : close connection */
939 av_freep(&c
->packet_buffer
);
943 c
->packet_buffer_ptr
+= len
;
944 if (c
->packet_buffer_ptr
>= c
->packet_buffer_end
) {
945 /* all the buffer was sent : wait for a new request */
946 av_freep(&c
->packet_buffer
);
947 c
->state
= RTSPSTATE_WAIT_REQUEST
;
951 case HTTPSTATE_READY
:
960 static int extract_rates(char *rates
, int ratelen
, const char *request
)
964 for (p
= request
; *p
&& *p
!= '\r' && *p
!= '\n'; ) {
965 if (strncasecmp(p
, "Pragma:", 7) == 0) {
966 const char *q
= p
+ 7;
968 while (*q
&& *q
!= '\n' && isspace(*q
))
971 if (strncasecmp(q
, "stream-switch-entry=", 20) == 0) {
977 memset(rates
, 0xff, ratelen
);
980 while (*q
&& *q
!= '\n' && *q
!= ':')
983 if (sscanf(q
, ":%d:%d", &stream_no
, &rate_no
) != 2) {
987 if (stream_no
< ratelen
&& stream_no
>= 0) {
988 rates
[stream_no
] = rate_no
;
991 while (*q
&& *q
!= '\n' && !isspace(*q
))
1008 static int find_stream_in_feed(FFStream
*feed
, AVCodecContext
*codec
, int bit_rate
)
1011 int best_bitrate
= 100000000;
1014 for (i
= 0; i
< feed
->nb_streams
; i
++) {
1015 AVCodecContext
*feed_codec
= feed
->streams
[i
]->codec
;
1017 if (feed_codec
->codec_id
!= codec
->codec_id
||
1018 feed_codec
->sample_rate
!= codec
->sample_rate
||
1019 feed_codec
->width
!= codec
->width
||
1020 feed_codec
->height
!= codec
->height
) {
1024 /* Potential stream */
1026 /* We want the fastest stream less than bit_rate, or the slowest
1027 * faster than bit_rate
1030 if (feed_codec
->bit_rate
<= bit_rate
) {
1031 if (best_bitrate
> bit_rate
|| feed_codec
->bit_rate
> best_bitrate
) {
1032 best_bitrate
= feed_codec
->bit_rate
;
1036 if (feed_codec
->bit_rate
< best_bitrate
) {
1037 best_bitrate
= feed_codec
->bit_rate
;
1046 static int modify_current_stream(HTTPContext
*c
, char *rates
)
1049 FFStream
*req
= c
->stream
;
1050 int action_required
= 0;
1052 /* Not much we can do for a feed */
1056 for (i
= 0; i
< req
->nb_streams
; i
++) {
1057 AVCodecContext
*codec
= req
->streams
[i
]->codec
;
1061 c
->switch_feed_streams
[i
] = req
->feed_streams
[i
];
1064 c
->switch_feed_streams
[i
] = find_stream_in_feed(req
->feed
, codec
, codec
->bit_rate
/ 2);
1067 /* Wants off or slow */
1068 c
->switch_feed_streams
[i
] = find_stream_in_feed(req
->feed
, codec
, codec
->bit_rate
/ 4);
1070 /* This doesn't work well when it turns off the only stream! */
1071 c
->switch_feed_streams
[i
] = -2;
1072 c
->feed_streams
[i
] = -2;
1077 if (c
->switch_feed_streams
[i
] >= 0 && c
->switch_feed_streams
[i
] != c
->feed_streams
[i
])
1078 action_required
= 1;
1081 return action_required
;
1085 static void do_switch_stream(HTTPContext
*c
, int i
)
1087 if (c
->switch_feed_streams
[i
] >= 0) {
1089 c
->feed_streams
[i
] = c
->switch_feed_streams
[i
];
1092 /* Now update the stream */
1094 c
->switch_feed_streams
[i
] = -1;
1097 /* XXX: factorize in utils.c ? */
1098 /* XXX: take care with different space meaning */
1099 static void skip_spaces(const char **pp
)
1103 while (*p
== ' ' || *p
== '\t')
1108 static void get_word(char *buf
, int buf_size
, const char **pp
)
1116 while (!isspace(*p
) && *p
!= '\0') {
1117 if ((q
- buf
) < buf_size
- 1)
1126 static int validate_acl(FFStream
*stream
, HTTPContext
*c
)
1128 enum IPAddressAction last_action
= IP_DENY
;
1130 struct in_addr
*src
= &c
->from_addr
.sin_addr
;
1131 unsigned long src_addr
= src
->s_addr
;
1133 for (acl
= stream
->acl
; acl
; acl
= acl
->next
) {
1134 if (src_addr
>= acl
->first
.s_addr
&& src_addr
<= acl
->last
.s_addr
) {
1135 return (acl
->action
== IP_ALLOW
) ? 1 : 0;
1137 last_action
= acl
->action
;
1140 /* Nothing matched, so return not the last action */
1141 return (last_action
== IP_DENY
) ? 1 : 0;
1144 /* compute the real filename of a file by matching it without its
1145 extensions to all the stream filenames */
1146 static void compute_real_filename(char *filename
, int max_size
)
1153 /* compute filename by matching without the file extensions */
1154 av_strlcpy(file1
, filename
, sizeof(file1
));
1155 p
= strrchr(file1
, '.');
1158 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
1159 av_strlcpy(file2
, stream
->filename
, sizeof(file2
));
1160 p
= strrchr(file2
, '.');
1163 if (!strcmp(file1
, file2
)) {
1164 av_strlcpy(filename
, stream
->filename
, max_size
);
1179 /* parse http request and prepare header */
1180 static int http_parse_request(HTTPContext
*c
)
1183 enum RedirType redir_type
;
1185 char info
[1024], filename
[1024];
1189 const char *mime_type
;
1193 char *useragent
= 0;
1196 get_word(cmd
, sizeof(cmd
), (const char **)&p
);
1197 av_strlcpy(c
->method
, cmd
, sizeof(c
->method
));
1199 if (!strcmp(cmd
, "GET"))
1201 else if (!strcmp(cmd
, "POST"))
1206 get_word(url
, sizeof(url
), (const char **)&p
);
1207 av_strlcpy(c
->url
, url
, sizeof(c
->url
));
1209 get_word(protocol
, sizeof(protocol
), (const char **)&p
);
1210 if (strcmp(protocol
, "HTTP/1.0") && strcmp(protocol
, "HTTP/1.1"))
1213 av_strlcpy(c
->protocol
, protocol
, sizeof(c
->protocol
));
1216 http_log("New connection: %s %s\n", cmd
, url
);
1218 /* find the filename and the optional info string in the request */
1219 p
= strchr(url
, '?');
1221 av_strlcpy(info
, p
, sizeof(info
));
1227 av_strlcpy(filename
, url
+ ((*url
== '/') ? 1 : 0), sizeof(filename
)-1);
1229 for (p
= c
->buffer
; *p
&& *p
!= '\r' && *p
!= '\n'; ) {
1230 if (strncasecmp(p
, "User-Agent:", 11) == 0) {
1232 if (*useragent
&& *useragent
!= '\n' && isspace(*useragent
))
1236 p
= strchr(p
, '\n');
1243 redir_type
= REDIR_NONE
;
1244 if (match_ext(filename
, "asx")) {
1245 redir_type
= REDIR_ASX
;
1246 filename
[strlen(filename
)-1] = 'f';
1247 } else if (match_ext(filename
, "asf") &&
1248 (!useragent
|| strncasecmp(useragent
, "NSPlayer", 8) != 0)) {
1249 /* if this isn't WMP or lookalike, return the redirector file */
1250 redir_type
= REDIR_ASF
;
1251 } else if (match_ext(filename
, "rpm,ram")) {
1252 redir_type
= REDIR_RAM
;
1253 strcpy(filename
+ strlen(filename
)-2, "m");
1254 } else if (match_ext(filename
, "rtsp")) {
1255 redir_type
= REDIR_RTSP
;
1256 compute_real_filename(filename
, sizeof(filename
) - 1);
1257 } else if (match_ext(filename
, "sdp")) {
1258 redir_type
= REDIR_SDP
;
1259 compute_real_filename(filename
, sizeof(filename
) - 1);
1262 // "redirect" / request to index.html
1263 if (!strlen(filename
))
1264 av_strlcpy(filename
, "index.html", sizeof(filename
) - 1);
1266 stream
= first_stream
;
1267 while (stream
!= NULL
) {
1268 if (!strcmp(stream
->filename
, filename
) && validate_acl(stream
, c
))
1270 stream
= stream
->next
;
1272 if (stream
== NULL
) {
1273 snprintf(msg
, sizeof(msg
), "File '%s' not found", url
);
1278 memcpy(c
->feed_streams
, stream
->feed_streams
, sizeof(c
->feed_streams
));
1279 memset(c
->switch_feed_streams
, -1, sizeof(c
->switch_feed_streams
));
1281 if (stream
->stream_type
== STREAM_TYPE_REDIRECT
) {
1282 c
->http_error
= 301;
1284 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "HTTP/1.0 301 Moved\r\n");
1285 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "Location: %s\r\n", stream
->feed_filename
);
1286 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "Content-type: text/html\r\n");
1287 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "\r\n");
1288 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "<html><head><title>Moved</title></head><body>\r\n");
1289 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "You should be <a href=\"%s\">redirected</a>.\r\n", stream
->feed_filename
);
1290 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "</body></html>\r\n");
1292 /* prepare output buffer */
1293 c
->buffer_ptr
= c
->buffer
;
1295 c
->state
= HTTPSTATE_SEND_HEADER
;
1299 /* If this is WMP, get the rate information */
1300 if (extract_rates(ratebuf
, sizeof(ratebuf
), c
->buffer
)) {
1301 if (modify_current_stream(c
, ratebuf
)) {
1302 for (i
= 0; i
< sizeof(c
->feed_streams
) / sizeof(c
->feed_streams
[0]); i
++) {
1303 if (c
->switch_feed_streams
[i
] >= 0)
1304 do_switch_stream(c
, i
);
1309 /* If already streaming this feed, do not let start another feeder. */
1310 if (stream
->feed_opened
) {
1311 snprintf(msg
, sizeof(msg
), "This feed is already being received.");
1315 if (c
->post
== 0 && stream
->stream_type
== STREAM_TYPE_LIVE
) {
1316 current_bandwidth
+= stream
->bandwidth
;
1319 if (c
->post
== 0 && max_bandwidth
< current_bandwidth
) {
1320 c
->http_error
= 200;
1322 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "HTTP/1.0 200 Server too busy\r\n");
1323 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "Content-type: text/html\r\n");
1324 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "\r\n");
1325 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "<html><head><title>Too busy</title></head><body>\r\n");
1326 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");
1327 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "<p>The bandwidth being served (including your stream) is %dkbit/sec, and this exceeds the limit of %dkbit/sec.</p>\r\n",
1328 current_bandwidth
, max_bandwidth
);
1329 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "</body></html>\r\n");
1331 /* prepare output buffer */
1332 c
->buffer_ptr
= c
->buffer
;
1334 c
->state
= HTTPSTATE_SEND_HEADER
;
1338 if (redir_type
!= REDIR_NONE
) {
1341 for (p
= c
->buffer
; *p
&& *p
!= '\r' && *p
!= '\n'; ) {
1342 if (strncasecmp(p
, "Host:", 5) == 0) {
1346 p
= strchr(p
, '\n');
1357 while (isspace(*hostinfo
))
1360 eoh
= strchr(hostinfo
, '\n');
1362 if (eoh
[-1] == '\r')
1365 if (eoh
- hostinfo
< sizeof(hostbuf
) - 1) {
1366 memcpy(hostbuf
, hostinfo
, eoh
- hostinfo
);
1367 hostbuf
[eoh
- hostinfo
] = 0;
1369 c
->http_error
= 200;
1371 switch(redir_type
) {
1373 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "HTTP/1.0 200 ASX Follows\r\n");
1374 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "Content-type: video/x-ms-asf\r\n");
1375 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "\r\n");
1376 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "<ASX Version=\"3\">\r\n");
1377 //q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<!-- Autogenerated by ffserver -->\r\n");
1378 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n",
1379 hostbuf
, filename
, info
);
1380 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "</ASX>\r\n");
1383 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "HTTP/1.0 200 RAM Follows\r\n");
1384 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "Content-type: audio/x-pn-realaudio\r\n");
1385 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "\r\n");
1386 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "# Autogenerated by ffserver\r\n");
1387 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "http://%s/%s%s\r\n",
1388 hostbuf
, filename
, info
);
1391 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "HTTP/1.0 200 ASF Redirect follows\r\n");
1392 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "Content-type: video/x-ms-asf\r\n");
1393 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "\r\n");
1394 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "[Reference]\r\n");
1395 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "Ref1=http://%s/%s%s\r\n",
1396 hostbuf
, filename
, info
);
1400 char hostname
[256], *p
;
1401 /* extract only hostname */
1402 av_strlcpy(hostname
, hostbuf
, sizeof(hostname
));
1403 p
= strrchr(hostname
, ':');
1406 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "HTTP/1.0 200 RTSP Redirect follows\r\n");
1407 /* XXX: incorrect mime type ? */
1408 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "Content-type: application/x-rtsp\r\n");
1409 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "\r\n");
1410 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "rtsp://%s:%d/%s\r\n",
1411 hostname
, ntohs(my_rtsp_addr
.sin_port
),
1418 int sdp_data_size
, len
;
1419 struct sockaddr_in my_addr
;
1421 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "HTTP/1.0 200 OK\r\n");
1422 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "Content-type: application/sdp\r\n");
1423 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "\r\n");
1425 len
= sizeof(my_addr
);
1426 getsockname(c
->fd
, (struct sockaddr
*)&my_addr
, &len
);
1428 /* XXX: should use a dynamic buffer */
1429 sdp_data_size
= prepare_sdp_description(stream
,
1432 if (sdp_data_size
> 0) {
1433 memcpy(q
, sdp_data
, sdp_data_size
);
1445 /* prepare output buffer */
1446 c
->buffer_ptr
= c
->buffer
;
1448 c
->state
= HTTPSTATE_SEND_HEADER
;
1454 snprintf(msg
, sizeof(msg
), "ASX/RAM file not handled");
1458 stream
->conns_served
++;
1460 /* XXX: add there authenticate and IP match */
1463 /* if post, it means a feed is being sent */
1464 if (!stream
->is_feed
) {
1465 /* However it might be a status report from WMP! Lets log the data
1466 * as it might come in handy one day
1471 for (p
= c
->buffer
; *p
&& *p
!= '\r' && *p
!= '\n'; ) {
1472 if (strncasecmp(p
, "Pragma: log-line=", 17) == 0) {
1476 if (strncasecmp(p
, "Pragma: client-id=", 18) == 0) {
1477 client_id
= strtol(p
+ 18, 0, 10);
1479 p
= strchr(p
, '\n');
1487 char *eol
= strchr(logline
, '\n');
1492 if (eol
[-1] == '\r')
1494 http_log("%.*s\n", (int) (eol
- logline
), logline
);
1495 c
->suppress_log
= 1;
1500 http_log("\nGot request:\n%s\n", c
->buffer
);
1503 if (client_id
&& extract_rates(ratebuf
, sizeof(ratebuf
), c
->buffer
)) {
1506 /* Now we have to find the client_id */
1507 for (wmpc
= first_http_ctx
; wmpc
; wmpc
= wmpc
->next
) {
1508 if (wmpc
->wmp_client_id
== client_id
)
1513 if (modify_current_stream(wmpc
, ratebuf
)) {
1514 wmpc
->switch_pending
= 1;
1519 snprintf(msg
, sizeof(msg
), "POST command not handled");
1523 if (http_start_receive_data(c
) < 0) {
1524 snprintf(msg
, sizeof(msg
), "could not open feed");
1528 c
->state
= HTTPSTATE_RECEIVE_DATA
;
1533 if (strcmp(stream
->filename
+ strlen(stream
->filename
) - 4, ".asf") == 0) {
1534 http_log("\nGot request:\n%s\n", c
->buffer
);
1538 if (c
->stream
->stream_type
== STREAM_TYPE_STATUS
)
1541 /* open input stream */
1542 if (open_input_stream(c
, info
) < 0) {
1543 snprintf(msg
, sizeof(msg
), "Input stream corresponding to '%s' not found", url
);
1547 /* prepare http header */
1549 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "HTTP/1.0 200 OK\r\n");
1550 mime_type
= c
->stream
->fmt
->mime_type
;
1552 mime_type
= "application/x-octet-stream";
1553 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "Pragma: no-cache\r\n");
1555 /* for asf, we need extra headers */
1556 if (!strcmp(c
->stream
->fmt
->name
,"asf_stream")) {
1557 /* Need to allocate a client id */
1559 c
->wmp_client_id
= av_random(&random_state
) & 0x7fffffff;
1561 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
);
1563 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "Content-Type: %s\r\n", mime_type
);
1564 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "\r\n");
1566 /* prepare output buffer */
1568 c
->buffer_ptr
= c
->buffer
;
1570 c
->state
= HTTPSTATE_SEND_HEADER
;
1573 c
->http_error
= 404;
1575 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "HTTP/1.0 404 Not Found\r\n");
1576 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "Content-type: %s\r\n", "text/html");
1577 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "\r\n");
1578 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "<HTML>\n");
1579 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n");
1580 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "<BODY>%s</BODY>\n", msg
);
1581 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "</HTML>\n");
1583 /* prepare output buffer */
1584 c
->buffer_ptr
= c
->buffer
;
1586 c
->state
= HTTPSTATE_SEND_HEADER
;
1590 c
->http_error
= 200; /* horrible : we use this value to avoid
1591 going to the send data state */
1592 c
->state
= HTTPSTATE_SEND_HEADER
;
1596 static void fmt_bytecount(ByteIOContext
*pb
, int64_t count
)
1598 static const char *suffix
= " kMGTP";
1601 for (s
= suffix
; count
>= 100000 && s
[1]; count
/= 1000, s
++) {
1604 url_fprintf(pb
, "%"PRId64
"%c", count
, *s
);
1607 static void compute_stats(HTTPContext
*c
)
1614 ByteIOContext pb1
, *pb
= &pb1
;
1616 if (url_open_dyn_buf(pb
) < 0) {
1617 /* XXX: return an error ? */
1618 c
->buffer_ptr
= c
->buffer
;
1619 c
->buffer_end
= c
->buffer
;
1623 url_fprintf(pb
, "HTTP/1.0 200 OK\r\n");
1624 url_fprintf(pb
, "Content-type: %s\r\n", "text/html");
1625 url_fprintf(pb
, "Pragma: no-cache\r\n");
1626 url_fprintf(pb
, "\r\n");
1628 url_fprintf(pb
, "<HEAD><TITLE>FFServer Status</TITLE>\n");
1629 if (c
->stream
->feed_filename
) {
1630 url_fprintf(pb
, "<link rel=\"shortcut icon\" href=\"%s\">\n", c
->stream
->feed_filename
);
1632 url_fprintf(pb
, "</HEAD>\n<BODY>");
1633 url_fprintf(pb
, "<H1>FFServer Status</H1>\n");
1635 url_fprintf(pb
, "<H2>Available Streams</H2>\n");
1636 url_fprintf(pb
, "<TABLE cellspacing=0 cellpadding=4>\n");
1637 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");
1638 stream
= first_stream
;
1639 while (stream
!= NULL
) {
1640 char sfilename
[1024];
1643 if (stream
->feed
!= stream
) {
1644 av_strlcpy(sfilename
, stream
->filename
, sizeof(sfilename
) - 10);
1645 eosf
= sfilename
+ strlen(sfilename
);
1646 if (eosf
- sfilename
>= 4) {
1647 if (strcmp(eosf
- 4, ".asf") == 0) {
1648 strcpy(eosf
- 4, ".asx");
1649 } else if (strcmp(eosf
- 3, ".rm") == 0) {
1650 strcpy(eosf
- 3, ".ram");
1651 } else if (stream
->fmt
== &rtp_muxer
) {
1652 /* generate a sample RTSP director if
1653 unicast. Generate an SDP redirector if
1655 eosf
= strrchr(sfilename
, '.');
1657 eosf
= sfilename
+ strlen(sfilename
);
1658 if (stream
->is_multicast
)
1659 strcpy(eosf
, ".sdp");
1661 strcpy(eosf
, ".rtsp");
1665 url_fprintf(pb
, "<TR><TD><A HREF=\"/%s\">%s</A> ",
1666 sfilename
, stream
->filename
);
1667 url_fprintf(pb
, "<td align=right> %d <td align=right> ",
1668 stream
->conns_served
);
1669 fmt_bytecount(pb
, stream
->bytes_served
);
1670 switch(stream
->stream_type
) {
1671 case STREAM_TYPE_LIVE
:
1673 int audio_bit_rate
= 0;
1674 int video_bit_rate
= 0;
1675 const char *audio_codec_name
= "";
1676 const char *video_codec_name
= "";
1677 const char *audio_codec_name_extra
= "";
1678 const char *video_codec_name_extra
= "";
1680 for(i
=0;i
<stream
->nb_streams
;i
++) {
1681 AVStream
*st
= stream
->streams
[i
];
1682 AVCodec
*codec
= avcodec_find_encoder(st
->codec
->codec_id
);
1683 switch(st
->codec
->codec_type
) {
1684 case CODEC_TYPE_AUDIO
:
1685 audio_bit_rate
+= st
->codec
->bit_rate
;
1687 if (*audio_codec_name
)
1688 audio_codec_name_extra
= "...";
1689 audio_codec_name
= codec
->name
;
1692 case CODEC_TYPE_VIDEO
:
1693 video_bit_rate
+= st
->codec
->bit_rate
;
1695 if (*video_codec_name
)
1696 video_codec_name_extra
= "...";
1697 video_codec_name
= codec
->name
;
1700 case CODEC_TYPE_DATA
:
1701 video_bit_rate
+= st
->codec
->bit_rate
;
1707 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",
1710 video_bit_rate
/ 1000, video_codec_name
, video_codec_name_extra
,
1711 audio_bit_rate
/ 1000, audio_codec_name
, audio_codec_name_extra
);
1713 url_fprintf(pb
, "<TD>%s", stream
->feed
->filename
);
1715 url_fprintf(pb
, "<TD>%s", stream
->feed_filename
);
1717 url_fprintf(pb
, "\n");
1721 url_fprintf(pb
, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
1725 stream
= stream
->next
;
1727 url_fprintf(pb
, "</TABLE>\n");
1729 stream
= first_stream
;
1730 while (stream
!= NULL
) {
1731 if (stream
->feed
== stream
) {
1732 url_fprintf(pb
, "<h2>Feed %s</h2>", stream
->filename
);
1734 url_fprintf(pb
, "Running as pid %d.\n", stream
->pid
);
1736 #if defined(linux) && !defined(CONFIG_NOCUTILS)
1741 /* This is somewhat linux specific I guess */
1742 snprintf(ps_cmd
, sizeof(ps_cmd
),
1743 "ps -o \"%%cpu,cputime\" --no-headers %d",
1746 pid_stat
= popen(ps_cmd
, "r");
1751 if (fscanf(pid_stat
, "%10s %64s", cpuperc
,
1753 url_fprintf(pb
, "Currently using %s%% of the cpu. Total time used %s.\n",
1761 url_fprintf(pb
, "<p>");
1763 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");
1765 for (i
= 0; i
< stream
->nb_streams
; i
++) {
1766 AVStream
*st
= stream
->streams
[i
];
1767 AVCodec
*codec
= avcodec_find_encoder(st
->codec
->codec_id
);
1768 const char *type
= "unknown";
1769 char parameters
[64];
1773 switch(st
->codec
->codec_type
) {
1774 case CODEC_TYPE_AUDIO
:
1776 snprintf(parameters
, sizeof(parameters
), "%d channel(s), %d Hz", st
->codec
->channels
, st
->codec
->sample_rate
);
1778 case CODEC_TYPE_VIDEO
:
1780 snprintf(parameters
, sizeof(parameters
), "%dx%d, q=%d-%d, fps=%d", st
->codec
->width
, st
->codec
->height
,
1781 st
->codec
->qmin
, st
->codec
->qmax
, st
->codec
->time_base
.den
/ st
->codec
->time_base
.num
);
1786 url_fprintf(pb
, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
1787 i
, type
, st
->codec
->bit_rate
/1000, codec
? codec
->name
: "", parameters
);
1789 url_fprintf(pb
, "</table>\n");
1792 stream
= stream
->next
;
1798 AVCodecContext
*enc
;
1802 stream
= first_feed
;
1803 while (stream
!= NULL
) {
1804 url_fprintf(pb
, "<H1>Feed '%s'</H1>\n", stream
->filename
);
1805 url_fprintf(pb
, "<TABLE>\n");
1806 url_fprintf(pb
, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n");
1807 for(i
=0;i
<stream
->nb_streams
;i
++) {
1808 AVStream
*st
= stream
->streams
[i
];
1809 FeedData
*fdata
= st
->priv_data
;
1812 avcodec_string(buf
, sizeof(buf
), enc
);
1813 avg
= fdata
->avg_frame_size
* (float)enc
->rate
* 8.0;
1814 if (enc
->codec
->type
== CODEC_TYPE_AUDIO
&& enc
->frame_size
> 0)
1815 avg
/= enc
->frame_size
;
1816 url_fprintf(pb
, "<TR><TD>%s <TD> %d <TD> %"PRId64
" <TD> %0.1f\n",
1817 buf
, enc
->frame_number
, fdata
->data_count
, avg
/ 1000.0);
1819 url_fprintf(pb
, "</TABLE>\n");
1820 stream
= stream
->next_feed
;
1825 /* connection status */
1826 url_fprintf(pb
, "<H2>Connection Status</H2>\n");
1828 url_fprintf(pb
, "Number of connections: %d / %d<BR>\n",
1829 nb_connections
, nb_max_connections
);
1831 url_fprintf(pb
, "Bandwidth in use: %dk / %dk<BR>\n",
1832 current_bandwidth
, max_bandwidth
);
1834 url_fprintf(pb
, "<TABLE>\n");
1835 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");
1836 c1
= first_http_ctx
;
1838 while (c1
!= NULL
) {
1844 for (j
= 0; j
< c1
->stream
->nb_streams
; j
++) {
1845 if (!c1
->stream
->feed
) {
1846 bitrate
+= c1
->stream
->streams
[j
]->codec
->bit_rate
;
1848 if (c1
->feed_streams
[j
] >= 0) {
1849 bitrate
+= c1
->stream
->feed
->streams
[c1
->feed_streams
[j
]]->codec
->bit_rate
;
1856 p
= inet_ntoa(c1
->from_addr
.sin_addr
);
1857 url_fprintf(pb
, "<TR><TD><B>%d</B><TD>%s%s<TD>%s<TD>%s<TD>%s<td align=right>",
1859 c1
->stream
? c1
->stream
->filename
: "",
1860 c1
->state
== HTTPSTATE_RECEIVE_DATA
? "(input)" : "",
1863 http_state
[c1
->state
]);
1864 fmt_bytecount(pb
, bitrate
);
1865 url_fprintf(pb
, "<td align=right>");
1866 fmt_bytecount(pb
, compute_datarate(&c1
->datarate
, c1
->data_count
) * 8);
1867 url_fprintf(pb
, "<td align=right>");
1868 fmt_bytecount(pb
, c1
->data_count
);
1869 url_fprintf(pb
, "\n");
1872 url_fprintf(pb
, "</TABLE>\n");
1877 url_fprintf(pb
, "<HR size=1 noshade>Generated at %s", p
);
1878 url_fprintf(pb
, "</BODY>\n</HTML>\n");
1880 len
= url_close_dyn_buf(pb
, &c
->pb_buffer
);
1881 c
->buffer_ptr
= c
->pb_buffer
;
1882 c
->buffer_end
= c
->pb_buffer
+ len
;
1885 /* check if the parser needs to be opened for stream i */
1886 static void open_parser(AVFormatContext
*s
, int i
)
1888 AVStream
*st
= s
->streams
[i
];
1891 if (!st
->codec
->codec
) {
1892 codec
= avcodec_find_decoder(st
->codec
->codec_id
);
1893 if (codec
&& (codec
->capabilities
& CODEC_CAP_PARSE_ONLY
)) {
1894 st
->codec
->parse_only
= 1;
1895 if (avcodec_open(st
->codec
, codec
) < 0) {
1896 st
->codec
->parse_only
= 0;
1902 static int open_input_stream(HTTPContext
*c
, const char *info
)
1905 char input_filename
[1024];
1910 /* find file name */
1911 if (c
->stream
->feed
) {
1912 strcpy(input_filename
, c
->stream
->feed
->feed_filename
);
1913 buf_size
= FFM_PACKET_SIZE
;
1914 /* compute position (absolute time) */
1915 if (find_info_tag(buf
, sizeof(buf
), "date", info
)) {
1916 stream_pos
= parse_date(buf
, 0);
1917 } else if (find_info_tag(buf
, sizeof(buf
), "buffer", info
)) {
1918 int prebuffer
= strtol(buf
, 0, 10);
1919 stream_pos
= av_gettime() - prebuffer
* (int64_t)1000000;
1921 stream_pos
= av_gettime() - c
->stream
->prebuffer
* (int64_t)1000;
1924 strcpy(input_filename
, c
->stream
->feed_filename
);
1926 /* compute position (relative time) */
1927 if (find_info_tag(buf
, sizeof(buf
), "date", info
)) {
1928 stream_pos
= parse_date(buf
, 1);
1933 if (input_filename
[0] == '\0')
1937 { time_t when
= stream_pos
/ 1000000;
1938 http_log("Stream pos = %"PRId64
", time=%s", stream_pos
, ctime(&when
));
1943 if (av_open_input_file(&s
, input_filename
, c
->stream
->ifmt
,
1944 buf_size
, c
->stream
->ap_in
) < 0) {
1945 http_log("%s not found", input_filename
);
1950 /* open each parser */
1951 for(i
=0;i
<s
->nb_streams
;i
++)
1954 /* choose stream as clock source (we favorize video stream if
1955 present) for packet sending */
1956 c
->pts_stream_index
= 0;
1957 for(i
=0;i
<c
->stream
->nb_streams
;i
++) {
1958 if (c
->pts_stream_index
== 0 &&
1959 c
->stream
->streams
[i
]->codec
->codec_type
== CODEC_TYPE_VIDEO
) {
1960 c
->pts_stream_index
= i
;
1965 if (c
->fmt_in
->iformat
->read_seek
) {
1966 c
->fmt_in
->iformat
->read_seek(c
->fmt_in
, 0, stream_pos
, 0);
1969 /* set the start time (needed for maxtime and RTP packet timing) */
1970 c
->start_time
= cur_time
;
1971 c
->first_pts
= AV_NOPTS_VALUE
;
1975 /* return the server clock (in us) */
1976 static int64_t get_server_clock(HTTPContext
*c
)
1978 /* compute current pts value from system time */
1979 return (cur_time
- c
->start_time
) * 1000;
1982 /* return the estimated time at which the current packet must be sent
1984 static int64_t get_packet_send_clock(HTTPContext
*c
)
1986 int bytes_left
, bytes_sent
, frame_bytes
;
1988 frame_bytes
= c
->cur_frame_bytes
;
1989 if (frame_bytes
<= 0) {
1992 bytes_left
= c
->buffer_end
- c
->buffer_ptr
;
1993 bytes_sent
= frame_bytes
- bytes_left
;
1994 return c
->cur_pts
+ (c
->cur_frame_duration
* bytes_sent
) / frame_bytes
;
1999 static int http_prepare_data(HTTPContext
*c
)
2002 AVFormatContext
*ctx
;
2004 av_freep(&c
->pb_buffer
);
2006 case HTTPSTATE_SEND_DATA_HEADER
:
2007 memset(&c
->fmt_ctx
, 0, sizeof(c
->fmt_ctx
));
2008 av_strlcpy(c
->fmt_ctx
.author
, c
->stream
->author
,
2009 sizeof(c
->fmt_ctx
.author
));
2010 av_strlcpy(c
->fmt_ctx
.comment
, c
->stream
->comment
,
2011 sizeof(c
->fmt_ctx
.comment
));
2012 av_strlcpy(c
->fmt_ctx
.copyright
, c
->stream
->copyright
,
2013 sizeof(c
->fmt_ctx
.copyright
));
2014 av_strlcpy(c
->fmt_ctx
.title
, c
->stream
->title
,
2015 sizeof(c
->fmt_ctx
.title
));
2017 /* open output stream by using specified codecs */
2018 c
->fmt_ctx
.oformat
= c
->stream
->fmt
;
2019 c
->fmt_ctx
.nb_streams
= c
->stream
->nb_streams
;
2020 for(i
=0;i
<c
->fmt_ctx
.nb_streams
;i
++) {
2023 st
= av_mallocz(sizeof(AVStream
));
2024 st
->codec
= avcodec_alloc_context();
2025 c
->fmt_ctx
.streams
[i
] = st
;
2026 /* if file or feed, then just take streams from FFStream struct */
2027 if (!c
->stream
->feed
||
2028 c
->stream
->feed
== c
->stream
)
2029 src
= c
->stream
->streams
[i
];
2031 src
= c
->stream
->feed
->streams
[c
->stream
->feed_streams
[i
]];
2035 st
->codec
->frame_number
= 0; /* XXX: should be done in
2036 AVStream, not in codec */
2037 /* I'm pretty sure that this is not correct...
2038 * However, without it, we crash
2040 st
->codec
->coded_frame
= &dummy_frame
;
2042 c
->got_key_frame
= 0;
2044 /* prepare header and save header data in a stream */
2045 if (url_open_dyn_buf(&c
->fmt_ctx
.pb
) < 0) {
2046 /* XXX: potential leak */
2049 c
->fmt_ctx
.pb
.is_streamed
= 1;
2051 av_set_parameters(&c
->fmt_ctx
, NULL
);
2052 if (av_write_header(&c
->fmt_ctx
) < 0)
2055 len
= url_close_dyn_buf(&c
->fmt_ctx
.pb
, &c
->pb_buffer
);
2056 c
->buffer_ptr
= c
->pb_buffer
;
2057 c
->buffer_end
= c
->pb_buffer
+ len
;
2059 c
->state
= HTTPSTATE_SEND_DATA
;
2060 c
->last_packet_sent
= 0;
2062 case HTTPSTATE_SEND_DATA
:
2063 /* find a new packet */
2067 /* read a packet from the input stream */
2068 if (c
->stream
->feed
) {
2069 ffm_set_write_index(c
->fmt_in
,
2070 c
->stream
->feed
->feed_write_index
,
2071 c
->stream
->feed
->feed_size
);
2074 if (c
->stream
->max_time
&&
2075 c
->stream
->max_time
+ c
->start_time
- cur_time
< 0) {
2076 /* We have timed out */
2077 c
->state
= HTTPSTATE_SEND_DATA_TRAILER
;
2080 if (av_read_frame(c
->fmt_in
, &pkt
) < 0) {
2081 if (c
->stream
->feed
&& c
->stream
->feed
->feed_opened
) {
2082 /* if coming from feed, it means we reached the end of the
2083 ffm file, so must wait for more data */
2084 c
->state
= HTTPSTATE_WAIT_FEED
;
2085 return 1; /* state changed */
2087 if (c
->stream
->loop
) {
2088 av_close_input_file(c
->fmt_in
);
2090 if (open_input_stream(c
, "") < 0)
2095 /* must send trailer now because eof or error */
2096 c
->state
= HTTPSTATE_SEND_DATA_TRAILER
;
2100 /* update first pts if needed */
2101 if (c
->first_pts
== AV_NOPTS_VALUE
) {
2102 c
->first_pts
= av_rescale_q(pkt
.dts
, c
->fmt_in
->streams
[pkt
.stream_index
]->time_base
, AV_TIME_BASE_Q
);
2103 c
->start_time
= cur_time
;
2105 /* send it to the appropriate stream */
2106 if (c
->stream
->feed
) {
2107 /* if coming from a feed, select the right stream */
2108 if (c
->switch_pending
) {
2109 c
->switch_pending
= 0;
2110 for(i
=0;i
<c
->stream
->nb_streams
;i
++) {
2111 if (c
->switch_feed_streams
[i
] == pkt
.stream_index
) {
2112 if (pkt
.flags
& PKT_FLAG_KEY
) {
2113 do_switch_stream(c
, i
);
2116 if (c
->switch_feed_streams
[i
] >= 0) {
2117 c
->switch_pending
= 1;
2121 for(i
=0;i
<c
->stream
->nb_streams
;i
++) {
2122 if (c
->feed_streams
[i
] == pkt
.stream_index
) {
2123 pkt
.stream_index
= i
;
2124 if (pkt
.flags
& PKT_FLAG_KEY
) {
2125 c
->got_key_frame
|= 1 << i
;
2127 /* See if we have all the key frames, then
2128 * we start to send. This logic is not quite
2129 * right, but it works for the case of a
2130 * single video stream with one or more
2131 * audio streams (for which every frame is
2132 * typically a key frame).
2134 if (!c
->stream
->send_on_key
||
2135 ((c
->got_key_frame
+ 1) >> c
->stream
->nb_streams
)) {
2141 AVCodecContext
*codec
;
2144 /* specific handling for RTP: we use several
2145 output stream (one for each RTP
2146 connection). XXX: need more abstract handling */
2147 if (c
->is_packetized
) {
2149 /* compute send time and duration */
2150 st
= c
->fmt_in
->streams
[pkt
.stream_index
];
2151 c
->cur_pts
= av_rescale_q(pkt
.dts
, st
->time_base
, AV_TIME_BASE_Q
);
2152 if (st
->start_time
!= AV_NOPTS_VALUE
)
2153 c
->cur_pts
-= av_rescale_q(st
->start_time
, st
->time_base
, AV_TIME_BASE_Q
);
2154 c
->cur_frame_duration
= av_rescale_q(pkt
.duration
, st
->time_base
, AV_TIME_BASE_Q
);
2156 printf("index=%d pts=%0.3f duration=%0.6f\n",
2158 (double)c
->cur_pts
/
2160 (double)c
->cur_frame_duration
/
2163 /* find RTP context */
2164 c
->packet_stream_index
= pkt
.stream_index
;
2165 ctx
= c
->rtp_ctx
[c
->packet_stream_index
];
2167 av_free_packet(&pkt
);
2170 codec
= ctx
->streams
[0]->codec
;
2171 /* only one stream per RTP connection */
2172 pkt
.stream_index
= 0;
2176 codec
= ctx
->streams
[pkt
.stream_index
]->codec
;
2179 codec
->coded_frame
->key_frame
= ((pkt
.flags
& PKT_FLAG_KEY
) != 0);
2180 if (c
->is_packetized
) {
2181 int max_packet_size
;
2182 if (c
->rtp_protocol
== RTSP_PROTOCOL_RTP_TCP
)
2183 max_packet_size
= RTSP_TCP_MAX_PACKET_SIZE
;
2185 max_packet_size
= url_get_max_packet_size(c
->rtp_handles
[c
->packet_stream_index
]);
2186 ret
= url_open_dyn_packet_buf(&ctx
->pb
, max_packet_size
);
2188 ret
= url_open_dyn_buf(&ctx
->pb
);
2191 /* XXX: potential leak */
2194 if (pkt
.dts
!= AV_NOPTS_VALUE
)
2195 pkt
.dts
= av_rescale_q(pkt
.dts
,
2196 c
->fmt_in
->streams
[pkt
.stream_index
]->time_base
,
2197 ctx
->streams
[pkt
.stream_index
]->time_base
);
2198 if (pkt
.pts
!= AV_NOPTS_VALUE
)
2199 pkt
.pts
= av_rescale_q(pkt
.pts
,
2200 c
->fmt_in
->streams
[pkt
.stream_index
]->time_base
,
2201 ctx
->streams
[pkt
.stream_index
]->time_base
);
2202 if (av_write_frame(ctx
, &pkt
)) {
2203 c
->state
= HTTPSTATE_SEND_DATA_TRAILER
;
2206 len
= url_close_dyn_buf(&ctx
->pb
, &c
->pb_buffer
);
2207 c
->cur_frame_bytes
= len
;
2208 c
->buffer_ptr
= c
->pb_buffer
;
2209 c
->buffer_end
= c
->pb_buffer
+ len
;
2211 codec
->frame_number
++;
2215 av_free_packet(&pkt
);
2221 case HTTPSTATE_SEND_DATA_TRAILER
:
2222 /* last packet test ? */
2223 if (c
->last_packet_sent
|| c
->is_packetized
)
2226 /* prepare header */
2227 if (url_open_dyn_buf(&ctx
->pb
) < 0) {
2228 /* XXX: potential leak */
2231 av_write_trailer(ctx
);
2232 len
= url_close_dyn_buf(&ctx
->pb
, &c
->pb_buffer
);
2233 c
->buffer_ptr
= c
->pb_buffer
;
2234 c
->buffer_end
= c
->pb_buffer
+ len
;
2236 c
->last_packet_sent
= 1;
2242 /* should convert the format at the same time */
2243 /* send data starting at c->buffer_ptr to the output connection
2244 (either UDP or TCP connection) */
2245 static int http_send_data(HTTPContext
*c
)
2250 if (c
->buffer_ptr
>= c
->buffer_end
) {
2251 ret
= http_prepare_data(c
);
2254 else if (ret
!= 0) {
2255 /* state change requested */
2259 if (c
->is_packetized
) {
2260 /* RTP data output */
2261 len
= c
->buffer_end
- c
->buffer_ptr
;
2263 /* fail safe - should never happen */
2265 c
->buffer_ptr
= c
->buffer_end
;
2268 len
= (c
->buffer_ptr
[0] << 24) |
2269 (c
->buffer_ptr
[1] << 16) |
2270 (c
->buffer_ptr
[2] << 8) |
2272 if (len
> (c
->buffer_end
- c
->buffer_ptr
))
2274 if ((get_packet_send_clock(c
) - get_server_clock(c
)) > 0) {
2275 /* nothing to send yet: we can wait */
2279 c
->data_count
+= len
;
2280 update_datarate(&c
->datarate
, c
->data_count
);
2282 c
->stream
->bytes_served
+= len
;
2284 if (c
->rtp_protocol
== RTSP_PROTOCOL_RTP_TCP
) {
2285 /* RTP packets are sent inside the RTSP TCP connection */
2286 ByteIOContext pb1
, *pb
= &pb1
;
2287 int interleaved_index
, size
;
2289 HTTPContext
*rtsp_c
;
2292 /* if no RTSP connection left, error */
2295 /* if already sending something, then wait. */
2296 if (rtsp_c
->state
!= RTSPSTATE_WAIT_REQUEST
) {
2299 if (url_open_dyn_buf(pb
) < 0)
2301 interleaved_index
= c
->packet_stream_index
* 2;
2302 /* RTCP packets are sent at odd indexes */
2303 if (c
->buffer_ptr
[1] == 200)
2304 interleaved_index
++;
2305 /* write RTSP TCP header */
2307 header
[1] = interleaved_index
;
2308 header
[2] = len
>> 8;
2310 put_buffer(pb
, header
, 4);
2311 /* write RTP packet data */
2313 put_buffer(pb
, c
->buffer_ptr
, len
);
2314 size
= url_close_dyn_buf(pb
, &c
->packet_buffer
);
2315 /* prepare asynchronous TCP sending */
2316 rtsp_c
->packet_buffer_ptr
= c
->packet_buffer
;
2317 rtsp_c
->packet_buffer_end
= c
->packet_buffer
+ size
;
2318 c
->buffer_ptr
+= len
;
2320 /* send everything we can NOW */
2321 len
= send(rtsp_c
->fd
, rtsp_c
->packet_buffer_ptr
,
2322 rtsp_c
->packet_buffer_end
- rtsp_c
->packet_buffer_ptr
, 0);
2324 rtsp_c
->packet_buffer_ptr
+= len
;
2326 if (rtsp_c
->packet_buffer_ptr
< rtsp_c
->packet_buffer_end
) {
2327 /* if we could not send all the data, we will
2328 send it later, so a new state is needed to
2329 "lock" the RTSP TCP connection */
2330 rtsp_c
->state
= RTSPSTATE_SEND_PACKET
;
2333 /* all data has been sent */
2334 av_freep(&c
->packet_buffer
);
2337 /* send RTP packet directly in UDP */
2339 url_write(c
->rtp_handles
[c
->packet_stream_index
],
2340 c
->buffer_ptr
, len
);
2341 c
->buffer_ptr
+= len
;
2342 /* here we continue as we can send several packets per 10 ms slot */
2345 /* TCP data output */
2346 len
= send(c
->fd
, c
->buffer_ptr
, c
->buffer_end
- c
->buffer_ptr
, 0);
2348 if (ff_neterrno() != FF_NETERROR(EAGAIN
) &&
2349 ff_neterrno() != FF_NETERROR(EINTR
)) {
2350 /* error : close connection */
2356 c
->buffer_ptr
+= len
;
2358 c
->data_count
+= len
;
2359 update_datarate(&c
->datarate
, c
->data_count
);
2361 c
->stream
->bytes_served
+= len
;
2369 static int http_start_receive_data(HTTPContext
*c
)
2373 if (c
->stream
->feed_opened
)
2376 /* Don't permit writing to this one */
2377 if (c
->stream
->readonly
)
2381 fd
= open(c
->stream
->feed_filename
, O_RDWR
);
2386 c
->stream
->feed_write_index
= ffm_read_write_index(fd
);
2387 c
->stream
->feed_size
= lseek(fd
, 0, SEEK_END
);
2388 lseek(fd
, 0, SEEK_SET
);
2390 /* init buffer input */
2391 c
->buffer_ptr
= c
->buffer
;
2392 c
->buffer_end
= c
->buffer
+ FFM_PACKET_SIZE
;
2393 c
->stream
->feed_opened
= 1;
2397 static int http_receive_data(HTTPContext
*c
)
2401 if (c
->buffer_end
> c
->buffer_ptr
) {
2404 len
= recv(c
->fd
, c
->buffer_ptr
, c
->buffer_end
- c
->buffer_ptr
, 0);
2406 if (ff_neterrno() != FF_NETERROR(EAGAIN
) &&
2407 ff_neterrno() != FF_NETERROR(EINTR
)) {
2408 /* error : close connection */
2411 } else if (len
== 0) {
2412 /* end of connection : close it */
2415 c
->buffer_ptr
+= len
;
2416 c
->data_count
+= len
;
2417 update_datarate(&c
->datarate
, c
->data_count
);
2421 if (c
->buffer_ptr
- c
->buffer
>= 2 && c
->data_count
> FFM_PACKET_SIZE
) {
2422 if (c
->buffer
[0] != 'f' ||
2423 c
->buffer
[1] != 'm') {
2424 http_log("Feed stream has become desynchronized -- disconnecting\n");
2429 if (c
->buffer_ptr
>= c
->buffer_end
) {
2430 FFStream
*feed
= c
->stream
;
2431 /* a packet has been received : write it in the store, except
2433 if (c
->data_count
> FFM_PACKET_SIZE
) {
2435 // printf("writing pos=0x%"PRIx64" size=0x%"PRIx64"\n", feed->feed_write_index, feed->feed_size);
2436 /* XXX: use llseek or url_seek */
2437 lseek(c
->feed_fd
, feed
->feed_write_index
, SEEK_SET
);
2438 write(c
->feed_fd
, c
->buffer
, FFM_PACKET_SIZE
);
2440 feed
->feed_write_index
+= FFM_PACKET_SIZE
;
2441 /* update file size */
2442 if (feed
->feed_write_index
> c
->stream
->feed_size
)
2443 feed
->feed_size
= feed
->feed_write_index
;
2445 /* handle wrap around if max file size reached */
2446 if (c
->stream
->feed_max_size
&& feed
->feed_write_index
>= c
->stream
->feed_max_size
)
2447 feed
->feed_write_index
= FFM_PACKET_SIZE
;
2450 ffm_write_write_index(c
->feed_fd
, feed
->feed_write_index
);
2452 /* wake up any waiting connections */
2453 for(c1
= first_http_ctx
; c1
!= NULL
; c1
= c1
->next
) {
2454 if (c1
->state
== HTTPSTATE_WAIT_FEED
&&
2455 c1
->stream
->feed
== c
->stream
->feed
) {
2456 c1
->state
= HTTPSTATE_SEND_DATA
;
2460 /* We have a header in our hands that contains useful data */
2462 AVInputFormat
*fmt_in
;
2463 ByteIOContext
*pb
= &s
.pb
;
2466 memset(&s
, 0, sizeof(s
));
2468 url_open_buf(pb
, c
->buffer
, c
->buffer_end
- c
->buffer
, URL_RDONLY
);
2469 pb
->buf_end
= c
->buffer_end
; /* ?? */
2470 pb
->is_streamed
= 1;
2472 /* use feed output format name to find corresponding input format */
2473 fmt_in
= av_find_input_format(feed
->fmt
->name
);
2477 if (fmt_in
->priv_data_size
> 0) {
2478 s
.priv_data
= av_mallocz(fmt_in
->priv_data_size
);
2484 if (fmt_in
->read_header(&s
, 0) < 0) {
2485 av_freep(&s
.priv_data
);
2489 /* Now we have the actual streams */
2490 if (s
.nb_streams
!= feed
->nb_streams
) {
2491 av_freep(&s
.priv_data
);
2494 for (i
= 0; i
< s
.nb_streams
; i
++) {
2495 memcpy(feed
->streams
[i
]->codec
,
2496 s
.streams
[i
]->codec
, sizeof(AVCodecContext
));
2498 av_freep(&s
.priv_data
);
2500 c
->buffer_ptr
= c
->buffer
;
2505 c
->stream
->feed_opened
= 0;
2510 /********************************************************************/
2513 static void rtsp_reply_header(HTTPContext
*c
, enum RTSPStatusCode error_number
)
2520 switch(error_number
) {
2521 case RTSP_STATUS_OK
:
2524 case RTSP_STATUS_METHOD
:
2525 str
= "Method Not Allowed";
2527 case RTSP_STATUS_BANDWIDTH
:
2528 str
= "Not Enough Bandwidth";
2530 case RTSP_STATUS_SESSION
:
2531 str
= "Session Not Found";
2533 case RTSP_STATUS_STATE
:
2534 str
= "Method Not Valid in This State";
2536 case RTSP_STATUS_AGGREGATE
:
2537 str
= "Aggregate operation not allowed";
2539 case RTSP_STATUS_ONLY_AGGREGATE
:
2540 str
= "Only aggregate operation allowed";
2542 case RTSP_STATUS_TRANSPORT
:
2543 str
= "Unsupported transport";
2545 case RTSP_STATUS_INTERNAL
:
2546 str
= "Internal Server Error";
2548 case RTSP_STATUS_SERVICE
:
2549 str
= "Service Unavailable";
2551 case RTSP_STATUS_VERSION
:
2552 str
= "RTSP Version not supported";
2555 str
= "Unknown Error";
2559 url_fprintf(c
->pb
, "RTSP/1.0 %d %s\r\n", error_number
, str
);
2560 url_fprintf(c
->pb
, "CSeq: %d\r\n", c
->seq
);
2562 /* output GMT time */
2566 p
= buf2
+ strlen(p
) - 1;
2569 url_fprintf(c
->pb
, "Date: %s GMT\r\n", buf2
);
2572 static void rtsp_reply_error(HTTPContext
*c
, enum RTSPStatusCode error_number
)
2574 rtsp_reply_header(c
, error_number
);
2575 url_fprintf(c
->pb
, "\r\n");
2578 static int rtsp_parse_request(HTTPContext
*c
)
2580 const char *p
, *p1
, *p2
;
2587 RTSPHeader header1
, *header
= &header1
;
2589 c
->buffer_ptr
[0] = '\0';
2592 get_word(cmd
, sizeof(cmd
), &p
);
2593 get_word(url
, sizeof(url
), &p
);
2594 get_word(protocol
, sizeof(protocol
), &p
);
2596 av_strlcpy(c
->method
, cmd
, sizeof(c
->method
));
2597 av_strlcpy(c
->url
, url
, sizeof(c
->url
));
2598 av_strlcpy(c
->protocol
, protocol
, sizeof(c
->protocol
));
2601 if (url_open_dyn_buf(c
->pb
) < 0) {
2602 /* XXX: cannot do more */
2603 c
->pb
= NULL
; /* safety */
2607 /* check version name */
2608 if (strcmp(protocol
, "RTSP/1.0") != 0) {
2609 rtsp_reply_error(c
, RTSP_STATUS_VERSION
);
2613 /* parse each header line */
2614 memset(header
, 0, sizeof(RTSPHeader
));
2615 /* skip to next line */
2616 while (*p
!= '\n' && *p
!= '\0')
2620 while (*p
!= '\0') {
2621 p1
= strchr(p
, '\n');
2625 if (p2
> p
&& p2
[-1] == '\r')
2627 /* skip empty line */
2631 if (len
> sizeof(line
) - 1)
2632 len
= sizeof(line
) - 1;
2633 memcpy(line
, p
, len
);
2635 rtsp_parse_line(header
, line
);
2639 /* handle sequence number */
2640 c
->seq
= header
->seq
;
2642 if (!strcmp(cmd
, "DESCRIBE")) {
2643 rtsp_cmd_describe(c
, url
);
2644 } else if (!strcmp(cmd
, "OPTIONS")) {
2645 rtsp_cmd_options(c
, url
);
2646 } else if (!strcmp(cmd
, "SETUP")) {
2647 rtsp_cmd_setup(c
, url
, header
);
2648 } else if (!strcmp(cmd
, "PLAY")) {
2649 rtsp_cmd_play(c
, url
, header
);
2650 } else if (!strcmp(cmd
, "PAUSE")) {
2651 rtsp_cmd_pause(c
, url
, header
);
2652 } else if (!strcmp(cmd
, "TEARDOWN")) {
2653 rtsp_cmd_teardown(c
, url
, header
);
2655 rtsp_reply_error(c
, RTSP_STATUS_METHOD
);
2658 len
= url_close_dyn_buf(c
->pb
, &c
->pb_buffer
);
2659 c
->pb
= NULL
; /* safety */
2661 /* XXX: cannot do more */
2664 c
->buffer_ptr
= c
->pb_buffer
;
2665 c
->buffer_end
= c
->pb_buffer
+ len
;
2666 c
->state
= RTSPSTATE_SEND_REPLY
;
2670 /* XXX: move that to rtsp.c, but would need to replace FFStream by
2672 static int prepare_sdp_description(FFStream
*stream
, uint8_t **pbuffer
,
2673 struct in_addr my_ip
)
2675 ByteIOContext pb1
, *pb
= &pb1
;
2676 int i
, payload_type
, port
, private_payload_type
, j
;
2677 const char *ipstr
, *title
, *mediatype
;
2680 if (url_open_dyn_buf(pb
) < 0)
2683 /* general media info */
2685 url_fprintf(pb
, "v=0\n");
2686 ipstr
= inet_ntoa(my_ip
);
2687 url_fprintf(pb
, "o=- 0 0 IN IP4 %s\n", ipstr
);
2688 title
= stream
->title
;
2689 if (title
[0] == '\0')
2691 url_fprintf(pb
, "s=%s\n", title
);
2692 if (stream
->comment
[0] != '\0')
2693 url_fprintf(pb
, "i=%s\n", stream
->comment
);
2694 if (stream
->is_multicast
) {
2695 url_fprintf(pb
, "c=IN IP4 %s\n", inet_ntoa(stream
->multicast_ip
));
2697 /* for each stream, we output the necessary info */
2698 private_payload_type
= RTP_PT_PRIVATE
;
2699 for(i
= 0; i
< stream
->nb_streams
; i
++) {
2700 st
= stream
->streams
[i
];
2701 if (st
->codec
->codec_id
== CODEC_ID_MPEG2TS
) {
2702 mediatype
= "video";
2704 switch(st
->codec
->codec_type
) {
2705 case CODEC_TYPE_AUDIO
:
2706 mediatype
= "audio";
2708 case CODEC_TYPE_VIDEO
:
2709 mediatype
= "video";
2712 mediatype
= "application";
2716 /* NOTE: the port indication is not correct in case of
2717 unicast. It is not an issue because RTSP gives it */
2718 payload_type
= rtp_get_payload_type(st
->codec
);
2719 if (payload_type
< 0)
2720 payload_type
= private_payload_type
++;
2721 if (stream
->is_multicast
) {
2722 port
= stream
->multicast_port
+ 2 * i
;
2726 url_fprintf(pb
, "m=%s %d RTP/AVP %d\n",
2727 mediatype
, port
, payload_type
);
2728 if (payload_type
>= RTP_PT_PRIVATE
) {
2729 /* for private payload type, we need to give more info */
2730 switch(st
->codec
->codec_id
) {
2731 case CODEC_ID_MPEG4
:
2734 url_fprintf(pb
, "a=rtpmap:%d MP4V-ES/%d\n",
2735 payload_type
, 90000);
2736 /* we must also add the mpeg4 header */
2737 data
= st
->codec
->extradata
;
2739 url_fprintf(pb
, "a=fmtp:%d config=", payload_type
);
2740 for(j
=0;j
<st
->codec
->extradata_size
;j
++) {
2741 url_fprintf(pb
, "%02x", data
[j
]);
2743 url_fprintf(pb
, "\n");
2748 /* XXX: add other codecs ? */
2752 url_fprintf(pb
, "a=control:streamid=%d\n", i
);
2754 return url_close_dyn_buf(pb
, pbuffer
);
2756 url_close_dyn_buf(pb
, pbuffer
);
2761 static void rtsp_cmd_options(HTTPContext
*c
, const char *url
)
2763 // rtsp_reply_header(c, RTSP_STATUS_OK);
2764 url_fprintf(c
->pb
, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK
, "OK");
2765 url_fprintf(c
->pb
, "CSeq: %d\r\n", c
->seq
);
2766 url_fprintf(c
->pb
, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
2767 url_fprintf(c
->pb
, "\r\n");
2770 static void rtsp_cmd_describe(HTTPContext
*c
, const char *url
)
2776 int content_length
, len
;
2777 struct sockaddr_in my_addr
;
2779 /* find which url is asked */
2780 url_split(NULL
, 0, NULL
, 0, NULL
, 0, NULL
, path1
, sizeof(path1
), url
);
2785 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
2786 if (!stream
->is_feed
&& stream
->fmt
== &rtp_muxer
&&
2787 !strcmp(path
, stream
->filename
)) {
2791 /* no stream found */
2792 rtsp_reply_error(c
, RTSP_STATUS_SERVICE
); /* XXX: right error ? */
2796 /* prepare the media description in sdp format */
2798 /* get the host IP */
2799 len
= sizeof(my_addr
);
2800 getsockname(c
->fd
, (struct sockaddr
*)&my_addr
, &len
);
2801 content_length
= prepare_sdp_description(stream
, &content
, my_addr
.sin_addr
);
2802 if (content_length
< 0) {
2803 rtsp_reply_error(c
, RTSP_STATUS_INTERNAL
);
2806 rtsp_reply_header(c
, RTSP_STATUS_OK
);
2807 url_fprintf(c
->pb
, "Content-Type: application/sdp\r\n");
2808 url_fprintf(c
->pb
, "Content-Length: %d\r\n", content_length
);
2809 url_fprintf(c
->pb
, "\r\n");
2810 put_buffer(c
->pb
, content
, content_length
);
2813 static HTTPContext
*find_rtp_session(const char *session_id
)
2817 if (session_id
[0] == '\0')
2820 for(c
= first_http_ctx
; c
!= NULL
; c
= c
->next
) {
2821 if (!strcmp(c
->session_id
, session_id
))
2827 static RTSPTransportField
*find_transport(RTSPHeader
*h
, enum RTSPProtocol protocol
)
2829 RTSPTransportField
*th
;
2832 for(i
=0;i
<h
->nb_transports
;i
++) {
2833 th
= &h
->transports
[i
];
2834 if (th
->protocol
== protocol
)
2840 static void rtsp_cmd_setup(HTTPContext
*c
, const char *url
,
2844 int stream_index
, port
;
2849 RTSPTransportField
*th
;
2850 struct sockaddr_in dest_addr
;
2851 RTSPActionServerSetup setup
;
2853 /* find which url is asked */
2854 url_split(NULL
, 0, NULL
, 0, NULL
, 0, NULL
, path1
, sizeof(path1
), url
);
2859 /* now check each stream */
2860 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
2861 if (!stream
->is_feed
&& stream
->fmt
== &rtp_muxer
) {
2862 /* accept aggregate filenames only if single stream */
2863 if (!strcmp(path
, stream
->filename
)) {
2864 if (stream
->nb_streams
!= 1) {
2865 rtsp_reply_error(c
, RTSP_STATUS_AGGREGATE
);
2872 for(stream_index
= 0; stream_index
< stream
->nb_streams
;
2874 snprintf(buf
, sizeof(buf
), "%s/streamid=%d",
2875 stream
->filename
, stream_index
);
2876 if (!strcmp(path
, buf
))
2881 /* no stream found */
2882 rtsp_reply_error(c
, RTSP_STATUS_SERVICE
); /* XXX: right error ? */
2886 /* generate session id if needed */
2887 if (h
->session_id
[0] == '\0') {
2888 snprintf(h
->session_id
, sizeof(h
->session_id
), "%08x%08x",
2889 av_random(&random_state
), av_random(&random_state
));
2892 /* find rtp session, and create it if none found */
2893 rtp_c
= find_rtp_session(h
->session_id
);
2895 /* always prefer UDP */
2896 th
= find_transport(h
, RTSP_PROTOCOL_RTP_UDP
);
2898 th
= find_transport(h
, RTSP_PROTOCOL_RTP_TCP
);
2900 rtsp_reply_error(c
, RTSP_STATUS_TRANSPORT
);
2905 rtp_c
= rtp_new_connection(&c
->from_addr
, stream
, h
->session_id
,
2908 rtsp_reply_error(c
, RTSP_STATUS_BANDWIDTH
);
2912 /* open input stream */
2913 if (open_input_stream(rtp_c
, "") < 0) {
2914 rtsp_reply_error(c
, RTSP_STATUS_INTERNAL
);
2919 /* test if stream is OK (test needed because several SETUP needs
2920 to be done for a given file) */
2921 if (rtp_c
->stream
!= stream
) {
2922 rtsp_reply_error(c
, RTSP_STATUS_SERVICE
);
2926 /* test if stream is already set up */
2927 if (rtp_c
->rtp_ctx
[stream_index
]) {
2928 rtsp_reply_error(c
, RTSP_STATUS_STATE
);
2932 /* check transport */
2933 th
= find_transport(h
, rtp_c
->rtp_protocol
);
2934 if (!th
|| (th
->protocol
== RTSP_PROTOCOL_RTP_UDP
&&
2935 th
->client_port_min
<= 0)) {
2936 rtsp_reply_error(c
, RTSP_STATUS_TRANSPORT
);
2940 /* setup default options */
2941 setup
.transport_option
[0] = '\0';
2942 dest_addr
= rtp_c
->from_addr
;
2943 dest_addr
.sin_port
= htons(th
->client_port_min
);
2946 if (rtp_new_av_stream(rtp_c
, stream_index
, &dest_addr
, c
) < 0) {
2947 rtsp_reply_error(c
, RTSP_STATUS_TRANSPORT
);
2951 /* now everything is OK, so we can send the connection parameters */
2952 rtsp_reply_header(c
, RTSP_STATUS_OK
);
2954 url_fprintf(c
->pb
, "Session: %s\r\n", rtp_c
->session_id
);
2956 switch(rtp_c
->rtp_protocol
) {
2957 case RTSP_PROTOCOL_RTP_UDP
:
2958 port
= rtp_get_local_port(rtp_c
->rtp_handles
[stream_index
]);
2959 url_fprintf(c
->pb
, "Transport: RTP/AVP/UDP;unicast;"
2960 "client_port=%d-%d;server_port=%d-%d",
2961 th
->client_port_min
, th
->client_port_min
+ 1,
2964 case RTSP_PROTOCOL_RTP_TCP
:
2965 url_fprintf(c
->pb
, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
2966 stream_index
* 2, stream_index
* 2 + 1);
2971 if (setup
.transport_option
[0] != '\0') {
2972 url_fprintf(c
->pb
, ";%s", setup
.transport_option
);
2974 url_fprintf(c
->pb
, "\r\n");
2977 url_fprintf(c
->pb
, "\r\n");
2981 /* find an rtp connection by using the session ID. Check consistency
2983 static HTTPContext
*find_rtp_session_with_url(const char *url
,
2984 const char *session_id
)
2992 rtp_c
= find_rtp_session(session_id
);
2996 /* find which url is asked */
2997 url_split(NULL
, 0, NULL
, 0, NULL
, 0, NULL
, path1
, sizeof(path1
), url
);
3001 if(!strcmp(path
, rtp_c
->stream
->filename
)) return rtp_c
;
3002 for(s
=0; s
<rtp_c
->stream
->nb_streams
; ++s
) {
3003 snprintf(buf
, sizeof(buf
), "%s/streamid=%d",
3004 rtp_c
->stream
->filename
, s
);
3005 if(!strncmp(path
, buf
, sizeof(buf
))) {
3006 // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
3013 static void rtsp_cmd_play(HTTPContext
*c
, const char *url
, RTSPHeader
*h
)
3017 rtp_c
= find_rtp_session_with_url(url
, h
->session_id
);
3019 rtsp_reply_error(c
, RTSP_STATUS_SESSION
);
3023 if (rtp_c
->state
!= HTTPSTATE_SEND_DATA
&&
3024 rtp_c
->state
!= HTTPSTATE_WAIT_FEED
&&
3025 rtp_c
->state
!= HTTPSTATE_READY
) {
3026 rtsp_reply_error(c
, RTSP_STATUS_STATE
);
3031 /* XXX: seek in stream */
3032 if (h
->range_start
!= AV_NOPTS_VALUE
) {
3033 printf("range_start=%0.3f\n", (double)h
->range_start
/ AV_TIME_BASE
);
3034 av_seek_frame(rtp_c
->fmt_in
, -1, h
->range_start
);
3038 rtp_c
->state
= HTTPSTATE_SEND_DATA
;
3040 /* now everything is OK, so we can send the connection parameters */
3041 rtsp_reply_header(c
, RTSP_STATUS_OK
);
3043 url_fprintf(c
->pb
, "Session: %s\r\n", rtp_c
->session_id
);
3044 url_fprintf(c
->pb
, "\r\n");
3047 static void rtsp_cmd_pause(HTTPContext
*c
, const char *url
, RTSPHeader
*h
)
3051 rtp_c
= find_rtp_session_with_url(url
, h
->session_id
);
3053 rtsp_reply_error(c
, RTSP_STATUS_SESSION
);
3057 if (rtp_c
->state
!= HTTPSTATE_SEND_DATA
&&
3058 rtp_c
->state
!= HTTPSTATE_WAIT_FEED
) {
3059 rtsp_reply_error(c
, RTSP_STATUS_STATE
);
3063 rtp_c
->state
= HTTPSTATE_READY
;
3064 rtp_c
->first_pts
= AV_NOPTS_VALUE
;
3065 /* now everything is OK, so we can send the connection parameters */
3066 rtsp_reply_header(c
, RTSP_STATUS_OK
);
3068 url_fprintf(c
->pb
, "Session: %s\r\n", rtp_c
->session_id
);
3069 url_fprintf(c
->pb
, "\r\n");
3072 static void rtsp_cmd_teardown(HTTPContext
*c
, const char *url
, RTSPHeader
*h
)
3075 char session_id
[32];
3077 rtp_c
= find_rtp_session_with_url(url
, h
->session_id
);
3079 rtsp_reply_error(c
, RTSP_STATUS_SESSION
);
3083 av_strlcpy(session_id
, rtp_c
->session_id
, sizeof(session_id
));
3085 /* abort the session */
3086 close_connection(rtp_c
);
3088 /* now everything is OK, so we can send the connection parameters */
3089 rtsp_reply_header(c
, RTSP_STATUS_OK
);
3091 url_fprintf(c
->pb
, "Session: %s\r\n", session_id
);
3092 url_fprintf(c
->pb
, "\r\n");
3096 /********************************************************************/
3099 static HTTPContext
*rtp_new_connection(struct sockaddr_in
*from_addr
,
3100 FFStream
*stream
, const char *session_id
,
3101 enum RTSPProtocol rtp_protocol
)
3103 HTTPContext
*c
= NULL
;
3104 const char *proto_str
;
3106 /* XXX: should output a warning page when coming
3107 close to the connection limit */
3108 if (nb_connections
>= nb_max_connections
)
3111 /* add a new connection */
3112 c
= av_mallocz(sizeof(HTTPContext
));
3117 c
->poll_entry
= NULL
;
3118 c
->from_addr
= *from_addr
;
3119 c
->buffer_size
= IOBUFFER_INIT_SIZE
;
3120 c
->buffer
= av_malloc(c
->buffer_size
);
3125 av_strlcpy(c
->session_id
, session_id
, sizeof(c
->session_id
));
3126 c
->state
= HTTPSTATE_READY
;
3127 c
->is_packetized
= 1;
3128 c
->rtp_protocol
= rtp_protocol
;
3130 /* protocol is shown in statistics */
3131 switch(c
->rtp_protocol
) {
3132 case RTSP_PROTOCOL_RTP_UDP_MULTICAST
:
3133 proto_str
= "MCAST";
3135 case RTSP_PROTOCOL_RTP_UDP
:
3138 case RTSP_PROTOCOL_RTP_TCP
:
3145 av_strlcpy(c
->protocol
, "RTP/", sizeof(c
->protocol
));
3146 av_strlcat(c
->protocol
, proto_str
, sizeof(c
->protocol
));
3148 current_bandwidth
+= stream
->bandwidth
;
3150 c
->next
= first_http_ctx
;
3162 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3163 command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
3165 static int rtp_new_av_stream(HTTPContext
*c
,
3166 int stream_index
, struct sockaddr_in
*dest_addr
,
3167 HTTPContext
*rtsp_c
)
3169 AVFormatContext
*ctx
;
3175 int max_packet_size
;
3177 /* now we can open the relevant output stream */
3178 ctx
= av_alloc_format_context();
3181 ctx
->oformat
= &rtp_muxer
;
3183 st
= av_mallocz(sizeof(AVStream
));
3186 st
->codec
= avcodec_alloc_context();
3187 ctx
->nb_streams
= 1;
3188 ctx
->streams
[0] = st
;
3190 if (!c
->stream
->feed
||
3191 c
->stream
->feed
== c
->stream
) {
3192 memcpy(st
, c
->stream
->streams
[stream_index
], sizeof(AVStream
));
3195 c
->stream
->feed
->streams
[c
->stream
->feed_streams
[stream_index
]],
3198 st
->priv_data
= NULL
;
3200 /* build destination RTP address */
3201 ipaddr
= inet_ntoa(dest_addr
->sin_addr
);
3203 switch(c
->rtp_protocol
) {
3204 case RTSP_PROTOCOL_RTP_UDP
:
3205 case RTSP_PROTOCOL_RTP_UDP_MULTICAST
:
3208 /* XXX: also pass as parameter to function ? */
3209 if (c
->stream
->is_multicast
) {
3211 ttl
= c
->stream
->multicast_ttl
;
3214 snprintf(ctx
->filename
, sizeof(ctx
->filename
),
3215 "rtp://%s:%d?multicast=1&ttl=%d",
3216 ipaddr
, ntohs(dest_addr
->sin_port
), ttl
);
3218 snprintf(ctx
->filename
, sizeof(ctx
->filename
),
3219 "rtp://%s:%d", ipaddr
, ntohs(dest_addr
->sin_port
));
3222 if (url_open(&h
, ctx
->filename
, URL_WRONLY
) < 0)
3224 c
->rtp_handles
[stream_index
] = h
;
3225 max_packet_size
= url_get_max_packet_size(h
);
3227 case RTSP_PROTOCOL_RTP_TCP
:
3230 max_packet_size
= RTSP_TCP_MAX_PACKET_SIZE
;
3236 http_log("%s:%d - - [%s] \"PLAY %s/streamid=%d %s\"\n",
3237 ipaddr
, ntohs(dest_addr
->sin_port
),
3239 c
->stream
->filename
, stream_index
, c
->protocol
);
3241 /* normally, no packets should be output here, but the packet size may be checked */
3242 if (url_open_dyn_packet_buf(&ctx
->pb
, max_packet_size
) < 0) {
3243 /* XXX: close stream */
3246 av_set_parameters(ctx
, NULL
);
3247 if (av_write_header(ctx
) < 0) {
3254 url_close_dyn_buf(&ctx
->pb
, &dummy_buf
);
3257 c
->rtp_ctx
[stream_index
] = ctx
;
3261 /********************************************************************/
3262 /* ffserver initialization */
3264 static AVStream
*add_av_stream1(FFStream
*stream
, AVCodecContext
*codec
)
3268 fst
= av_mallocz(sizeof(AVStream
));
3271 fst
->codec
= avcodec_alloc_context();
3272 fst
->priv_data
= av_mallocz(sizeof(FeedData
));
3273 memcpy(fst
->codec
, codec
, sizeof(AVCodecContext
));
3274 fst
->codec
->coded_frame
= &dummy_frame
;
3275 fst
->index
= stream
->nb_streams
;
3276 av_set_pts_info(fst
, 33, 1, 90000);
3277 stream
->streams
[stream
->nb_streams
++] = fst
;
3281 /* return the stream number in the feed */
3282 static int add_av_stream(FFStream
*feed
, AVStream
*st
)
3285 AVCodecContext
*av
, *av1
;
3289 for(i
=0;i
<feed
->nb_streams
;i
++) {
3290 st
= feed
->streams
[i
];
3292 if (av1
->codec_id
== av
->codec_id
&&
3293 av1
->codec_type
== av
->codec_type
&&
3294 av1
->bit_rate
== av
->bit_rate
) {
3296 switch(av
->codec_type
) {
3297 case CODEC_TYPE_AUDIO
:
3298 if (av1
->channels
== av
->channels
&&
3299 av1
->sample_rate
== av
->sample_rate
)
3302 case CODEC_TYPE_VIDEO
:
3303 if (av1
->width
== av
->width
&&
3304 av1
->height
== av
->height
&&
3305 av1
->time_base
.den
== av
->time_base
.den
&&
3306 av1
->time_base
.num
== av
->time_base
.num
&&
3307 av1
->gop_size
== av
->gop_size
)
3316 fst
= add_av_stream1(feed
, av
);
3319 return feed
->nb_streams
- 1;
3324 static void remove_stream(FFStream
*stream
)
3328 while (*ps
!= NULL
) {
3329 if (*ps
== stream
) {
3337 /* specific mpeg4 handling : we extract the raw parameters */
3338 static void extract_mpeg4_header(AVFormatContext
*infile
)
3340 int mpeg4_count
, i
, size
;
3346 for(i
=0;i
<infile
->nb_streams
;i
++) {
3347 st
= infile
->streams
[i
];
3348 if (st
->codec
->codec_id
== CODEC_ID_MPEG4
&&
3349 st
->codec
->extradata_size
== 0) {
3356 printf("MPEG4 without extra data: trying to find header in %s\n", infile
->filename
);
3357 while (mpeg4_count
> 0) {
3358 if (av_read_packet(infile
, &pkt
) < 0)
3360 st
= infile
->streams
[pkt
.stream_index
];
3361 if (st
->codec
->codec_id
== CODEC_ID_MPEG4
&&
3362 st
->codec
->extradata_size
== 0) {
3363 av_freep(&st
->codec
->extradata
);
3364 /* fill extradata with the header */
3365 /* XXX: we make hard suppositions here ! */
3367 while (p
< pkt
.data
+ pkt
.size
- 4) {
3368 /* stop when vop header is found */
3369 if (p
[0] == 0x00 && p
[1] == 0x00 &&
3370 p
[2] == 0x01 && p
[3] == 0xb6) {
3371 size
= p
- pkt
.data
;
3372 // av_hex_dump_log(infile, AV_LOG_DEBUG, pkt.data, size);
3373 st
->codec
->extradata
= av_malloc(size
);
3374 st
->codec
->extradata_size
= size
;
3375 memcpy(st
->codec
->extradata
, pkt
.data
, size
);
3382 av_free_packet(&pkt
);
3386 /* compute the needed AVStream for each file */
3387 static void build_file_streams(void)
3389 FFStream
*stream
, *stream_next
;
3390 AVFormatContext
*infile
;
3393 /* gather all streams */
3394 for(stream
= first_stream
; stream
!= NULL
; stream
= stream_next
) {
3395 stream_next
= stream
->next
;
3396 if (stream
->stream_type
== STREAM_TYPE_LIVE
&&
3398 /* the stream comes from a file */
3399 /* try to open the file */
3401 stream
->ap_in
= av_mallocz(sizeof(AVFormatParameters
));
3402 if (stream
->fmt
== &rtp_muxer
) {
3403 /* specific case : if transport stream output to RTP,
3404 we use a raw transport stream reader */
3405 stream
->ap_in
->mpeg2ts_raw
= 1;
3406 stream
->ap_in
->mpeg2ts_compute_pcr
= 1;
3409 if (av_open_input_file(&infile
, stream
->feed_filename
,
3410 stream
->ifmt
, 0, stream
->ap_in
) < 0) {
3411 http_log("%s not found", stream
->feed_filename
);
3412 /* remove stream (no need to spend more time on it) */
3414 remove_stream(stream
);
3416 /* find all the AVStreams inside and reference them in
3418 if (av_find_stream_info(infile
) < 0) {
3419 http_log("Could not find codec parameters from '%s'",
3420 stream
->feed_filename
);
3421 av_close_input_file(infile
);
3424 extract_mpeg4_header(infile
);
3426 for(i
=0;i
<infile
->nb_streams
;i
++) {
3427 add_av_stream1(stream
, infile
->streams
[i
]->codec
);
3429 av_close_input_file(infile
);
3435 /* compute the needed AVStream for each feed */
3436 static void build_feed_streams(void)
3438 FFStream
*stream
, *feed
;
3441 /* gather all streams */
3442 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
3443 feed
= stream
->feed
;
3445 if (!stream
->is_feed
) {
3446 /* we handle a stream coming from a feed */
3447 for(i
=0;i
<stream
->nb_streams
;i
++) {
3448 stream
->feed_streams
[i
] = add_av_stream(feed
, stream
->streams
[i
]);
3454 /* gather all streams */
3455 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
3456 feed
= stream
->feed
;
3458 if (stream
->is_feed
) {
3459 for(i
=0;i
<stream
->nb_streams
;i
++) {
3460 stream
->feed_streams
[i
] = i
;
3466 /* create feed files if needed */
3467 for(feed
= first_feed
; feed
!= NULL
; feed
= feed
->next_feed
) {
3470 if (url_exist(feed
->feed_filename
)) {
3471 /* See if it matches */
3475 if (av_open_input_file(&s
, feed
->feed_filename
, NULL
, FFM_PACKET_SIZE
, NULL
) >= 0) {
3476 /* Now see if it matches */
3477 if (s
->nb_streams
== feed
->nb_streams
) {
3479 for(i
=0;i
<s
->nb_streams
;i
++) {
3481 sf
= feed
->streams
[i
];
3484 if (sf
->index
!= ss
->index
||
3486 printf("Index & Id do not match for stream %d (%s)\n",
3487 i
, feed
->feed_filename
);
3490 AVCodecContext
*ccf
, *ccs
;
3494 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3496 if (CHECK_CODEC(codec
) || CHECK_CODEC(codec_type
)) {
3497 printf("Codecs do not match for stream %d\n", i
);
3499 } else if (CHECK_CODEC(bit_rate
) || CHECK_CODEC(flags
)) {
3500 printf("Codec bitrates do not match for stream %d\n", i
);
3502 } else if (ccf
->codec_type
== CODEC_TYPE_VIDEO
) {
3503 if (CHECK_CODEC(time_base
.den
) ||
3504 CHECK_CODEC(time_base
.num
) ||
3505 CHECK_CODEC(width
) ||
3506 CHECK_CODEC(height
)) {
3507 printf("Codec width, height and framerate do not match for stream %d\n", i
);
3510 } else if (ccf
->codec_type
== CODEC_TYPE_AUDIO
) {
3511 if (CHECK_CODEC(sample_rate
) ||
3512 CHECK_CODEC(channels
) ||
3513 CHECK_CODEC(frame_size
)) {
3514 printf("Codec sample_rate, channels, frame_size do not match for stream %d\n", i
);
3518 printf("Unknown codec type\n");
3527 printf("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3528 feed
->feed_filename
, s
->nb_streams
, feed
->nb_streams
);
3531 av_close_input_file(s
);
3533 printf("Deleting feed file '%s' as it appears to be corrupt\n",
3534 feed
->feed_filename
);
3537 if (feed
->readonly
) {
3538 printf("Unable to delete feed file '%s' as it is marked readonly\n",
3539 feed
->feed_filename
);
3542 unlink(feed
->feed_filename
);
3545 if (!url_exist(feed
->feed_filename
)) {
3546 AVFormatContext s1
, *s
= &s1
;
3548 if (feed
->readonly
) {
3549 printf("Unable to create feed file '%s' as it is marked readonly\n",
3550 feed
->feed_filename
);
3554 /* only write the header of the ffm file */
3555 if (url_fopen(&s
->pb
, feed
->feed_filename
, URL_WRONLY
) < 0) {
3556 fprintf(stderr
, "Could not open output feed file '%s'\n",
3557 feed
->feed_filename
);
3560 s
->oformat
= feed
->fmt
;
3561 s
->nb_streams
= feed
->nb_streams
;
3562 for(i
=0;i
<s
->nb_streams
;i
++) {
3564 st
= feed
->streams
[i
];
3567 av_set_parameters(s
, NULL
);
3568 if (av_write_header(s
) < 0) {
3569 fprintf(stderr
, "Container doesn't supports the required parameters\n");
3572 /* XXX: need better api */
3573 av_freep(&s
->priv_data
);
3576 /* get feed size and write index */
3577 fd
= open(feed
->feed_filename
, O_RDONLY
);
3579 fprintf(stderr
, "Could not open output feed file '%s'\n",
3580 feed
->feed_filename
);
3584 feed
->feed_write_index
= ffm_read_write_index(fd
);
3585 feed
->feed_size
= lseek(fd
, 0, SEEK_END
);
3586 /* ensure that we do not wrap before the end of file */
3587 if (feed
->feed_max_size
&& feed
->feed_max_size
< feed
->feed_size
)
3588 feed
->feed_max_size
= feed
->feed_size
;
3594 /* compute the bandwidth used by each stream */
3595 static void compute_bandwidth(void)
3600 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
3602 for(i
=0;i
<stream
->nb_streams
;i
++) {
3603 AVStream
*st
= stream
->streams
[i
];
3604 switch(st
->codec
->codec_type
) {
3605 case CODEC_TYPE_AUDIO
:
3606 case CODEC_TYPE_VIDEO
:
3607 bandwidth
+= st
->codec
->bit_rate
;
3613 stream
->bandwidth
= (bandwidth
+ 999) / 1000;
3617 static void get_arg(char *buf
, int buf_size
, const char **pp
)
3624 while (isspace(*p
)) p
++;
3627 if (*p
== '\"' || *p
== '\'')
3639 if ((q
- buf
) < buf_size
- 1)
3644 if (quote
&& *p
== quote
)
3649 /* add a codec and set the default parameters */
3650 static void add_codec(FFStream
*stream
, AVCodecContext
*av
)
3654 /* compute default parameters */
3655 switch(av
->codec_type
) {
3656 case CODEC_TYPE_AUDIO
:
3657 if (av
->bit_rate
== 0)
3658 av
->bit_rate
= 64000;
3659 if (av
->sample_rate
== 0)
3660 av
->sample_rate
= 22050;
3661 if (av
->channels
== 0)
3664 case CODEC_TYPE_VIDEO
:
3665 if (av
->bit_rate
== 0)
3666 av
->bit_rate
= 64000;
3667 if (av
->time_base
.num
== 0){
3668 av
->time_base
.den
= 5;
3669 av
->time_base
.num
= 1;
3671 if (av
->width
== 0 || av
->height
== 0) {
3675 /* Bitrate tolerance is less for streaming */
3676 if (av
->bit_rate_tolerance
== 0)
3677 av
->bit_rate_tolerance
= av
->bit_rate
/ 4;
3682 if (av
->max_qdiff
== 0)
3684 av
->qcompress
= 0.5;
3687 if (!av
->nsse_weight
)
3688 av
->nsse_weight
= 8;
3690 av
->frame_skip_cmp
= FF_CMP_DCTMAX
;
3691 av
->me_method
= ME_EPZS
;
3692 av
->rc_buffer_aggressivity
= 1.0;
3695 av
->rc_eq
= "tex^qComp";
3696 if (!av
->i_quant_factor
)
3697 av
->i_quant_factor
= -0.8;
3698 if (!av
->b_quant_factor
)
3699 av
->b_quant_factor
= 1.25;
3700 if (!av
->b_quant_offset
)
3701 av
->b_quant_offset
= 1.25;
3702 if (!av
->rc_max_rate
)
3703 av
->rc_max_rate
= av
->bit_rate
* 2;
3705 if (av
->rc_max_rate
&& !av
->rc_buffer_size
) {
3706 av
->rc_buffer_size
= av
->rc_max_rate
;
3715 st
= av_mallocz(sizeof(AVStream
));
3718 st
->codec
= avcodec_alloc_context();
3719 stream
->streams
[stream
->nb_streams
++] = st
;
3720 memcpy(st
->codec
, av
, sizeof(AVCodecContext
));
3723 static int opt_audio_codec(const char *arg
)
3729 if (!strcmp(p
->name
, arg
) && p
->type
== CODEC_TYPE_AUDIO
)
3734 return CODEC_ID_NONE
;
3740 static int opt_video_codec(const char *arg
)
3746 if (!strcmp(p
->name
, arg
) && p
->type
== CODEC_TYPE_VIDEO
)
3751 return CODEC_ID_NONE
;
3757 /* simplistic plugin support */
3760 static void load_module(const char *filename
)
3763 void (*init_func
)(void);
3764 dll
= dlopen(filename
, RTLD_NOW
);
3766 fprintf(stderr
, "Could not load module '%s' - %s\n",
3767 filename
, dlerror());
3771 init_func
= dlsym(dll
, "ffserver_module_init");
3774 "%s: init function 'ffserver_module_init()' not found\n",
3783 static int parse_ffconfig(const char *filename
)
3790 int val
, errors
, line_num
;
3791 FFStream
**last_stream
, *stream
, *redirect
;
3792 FFStream
**last_feed
, *feed
;
3793 AVCodecContext audio_enc
, video_enc
;
3794 int audio_id
, video_id
;
3796 f
= fopen(filename
, "r");
3804 first_stream
= NULL
;
3805 last_stream
= &first_stream
;
3807 last_feed
= &first_feed
;
3811 audio_id
= CODEC_ID_NONE
;
3812 video_id
= CODEC_ID_NONE
;
3814 if (fgets(line
, sizeof(line
), f
) == NULL
)
3820 if (*p
== '\0' || *p
== '#')
3823 get_arg(cmd
, sizeof(cmd
), &p
);
3825 if (!strcasecmp(cmd
, "Port")) {
3826 get_arg(arg
, sizeof(arg
), &p
);
3828 if (val
< 1 || val
> 65536) {
3829 fprintf(stderr
, "%s:%d: Invalid port: %s\n",
3830 filename
, line_num
, arg
);
3833 my_http_addr
.sin_port
= htons(val
);
3834 } else if (!strcasecmp(cmd
, "BindAddress")) {
3835 get_arg(arg
, sizeof(arg
), &p
);
3836 if (resolve_host(&my_http_addr
.sin_addr
, arg
) != 0) {
3837 fprintf(stderr
, "%s:%d: Invalid host/IP address: %s\n",
3838 filename
, line_num
, arg
);
3841 } else if (!strcasecmp(cmd
, "NoDaemon")) {
3842 ffserver_daemon
= 0;
3843 } else if (!strcasecmp(cmd
, "RTSPPort")) {
3844 get_arg(arg
, sizeof(arg
), &p
);
3846 if (val
< 1 || val
> 65536) {
3847 fprintf(stderr
, "%s:%d: Invalid port: %s\n",
3848 filename
, line_num
, arg
);
3851 my_rtsp_addr
.sin_port
= htons(atoi(arg
));
3852 } else if (!strcasecmp(cmd
, "RTSPBindAddress")) {
3853 get_arg(arg
, sizeof(arg
), &p
);
3854 if (resolve_host(&my_rtsp_addr
.sin_addr
, arg
) != 0) {
3855 fprintf(stderr
, "%s:%d: Invalid host/IP address: %s\n",
3856 filename
, line_num
, arg
);
3859 } else if (!strcasecmp(cmd
, "MaxClients")) {
3860 get_arg(arg
, sizeof(arg
), &p
);
3862 if (val
< 1 || val
> HTTP_MAX_CONNECTIONS
) {
3863 fprintf(stderr
, "%s:%d: Invalid MaxClients: %s\n",
3864 filename
, line_num
, arg
);
3867 nb_max_connections
= val
;
3869 } else if (!strcasecmp(cmd
, "MaxBandwidth")) {
3870 get_arg(arg
, sizeof(arg
), &p
);
3872 if (val
< 10 || val
> 100000) {
3873 fprintf(stderr
, "%s:%d: Invalid MaxBandwidth: %s\n",
3874 filename
, line_num
, arg
);
3877 max_bandwidth
= val
;
3879 } else if (!strcasecmp(cmd
, "CustomLog")) {
3880 get_arg(logfilename
, sizeof(logfilename
), &p
);
3881 } else if (!strcasecmp(cmd
, "<Feed")) {
3882 /*********************************************/
3883 /* Feed related options */
3885 if (stream
|| feed
) {
3886 fprintf(stderr
, "%s:%d: Already in a tag\n",
3887 filename
, line_num
);
3889 feed
= av_mallocz(sizeof(FFStream
));
3890 /* add in stream list */
3891 *last_stream
= feed
;
3892 last_stream
= &feed
->next
;
3893 /* add in feed list */
3895 last_feed
= &feed
->next_feed
;
3897 get_arg(feed
->filename
, sizeof(feed
->filename
), &p
);
3898 q
= strrchr(feed
->filename
, '>');
3901 feed
->fmt
= guess_format("ffm", NULL
, NULL
);
3902 /* defaut feed file */
3903 snprintf(feed
->feed_filename
, sizeof(feed
->feed_filename
),
3904 "/tmp/%s.ffm", feed
->filename
);
3905 feed
->feed_max_size
= 5 * 1024 * 1024;
3907 feed
->feed
= feed
; /* self feeding :-) */
3909 } else if (!strcasecmp(cmd
, "Launch")) {
3913 feed
->child_argv
= (char **) av_mallocz(64 * sizeof(char *));
3915 for (i
= 0; i
< 62; i
++) {
3916 get_arg(arg
, sizeof(arg
), &p
);
3920 feed
->child_argv
[i
] = av_strdup(arg
);
3923 feed
->child_argv
[i
] = av_malloc(30 + strlen(feed
->filename
));
3925 snprintf(feed
->child_argv
[i
], 30+strlen(feed
->filename
),
3927 (my_http_addr
.sin_addr
.s_addr
== INADDR_ANY
) ? "127.0.0.1" :
3928 inet_ntoa(my_http_addr
.sin_addr
),
3929 ntohs(my_http_addr
.sin_port
), feed
->filename
);
3934 fprintf(stdout
, "Launch commandline: ");
3935 for (j
= 0; j
<= i
; j
++)
3936 fprintf(stdout
, "%s ", feed
->child_argv
[j
]);
3937 fprintf(stdout
, "\n");
3940 } else if (!strcasecmp(cmd
, "ReadOnlyFile")) {
3942 get_arg(feed
->feed_filename
, sizeof(feed
->feed_filename
), &p
);
3944 } else if (stream
) {
3945 get_arg(stream
->feed_filename
, sizeof(stream
->feed_filename
), &p
);
3947 } else if (!strcasecmp(cmd
, "File")) {
3949 get_arg(feed
->feed_filename
, sizeof(feed
->feed_filename
), &p
);
3950 } else if (stream
) {
3951 get_arg(stream
->feed_filename
, sizeof(stream
->feed_filename
), &p
);
3953 } else if (!strcasecmp(cmd
, "FileMaxSize")) {
3958 get_arg(arg
, sizeof(arg
), &p
);
3960 fsize
= strtod(p1
, (char **)&p1
);
3961 switch(toupper(*p1
)) {
3966 fsize
*= 1024 * 1024;
3969 fsize
*= 1024 * 1024 * 1024;
3972 feed
->feed_max_size
= (int64_t)fsize
;
3974 } else if (!strcasecmp(cmd
, "</Feed>")) {
3976 fprintf(stderr
, "%s:%d: No corresponding <Feed> for </Feed>\n",
3977 filename
, line_num
);
3981 } else if (!strcasecmp(cmd
, "<Stream")) {
3982 /*********************************************/
3983 /* Stream related options */
3985 if (stream
|| feed
) {
3986 fprintf(stderr
, "%s:%d: Already in a tag\n",
3987 filename
, line_num
);
3989 stream
= av_mallocz(sizeof(FFStream
));
3990 *last_stream
= stream
;
3991 last_stream
= &stream
->next
;
3993 get_arg(stream
->filename
, sizeof(stream
->filename
), &p
);
3994 q
= strrchr(stream
->filename
, '>');
3997 stream
->fmt
= guess_stream_format(NULL
, stream
->filename
, NULL
);
3998 memset(&audio_enc
, 0, sizeof(AVCodecContext
));
3999 memset(&video_enc
, 0, sizeof(AVCodecContext
));
4000 audio_id
= CODEC_ID_NONE
;
4001 video_id
= CODEC_ID_NONE
;
4003 audio_id
= stream
->fmt
->audio_codec
;
4004 video_id
= stream
->fmt
->video_codec
;
4007 } else if (!strcasecmp(cmd
, "Feed")) {
4008 get_arg(arg
, sizeof(arg
), &p
);
4013 while (sfeed
!= NULL
) {
4014 if (!strcmp(sfeed
->filename
, arg
))
4016 sfeed
= sfeed
->next_feed
;
4019 fprintf(stderr
, "%s:%d: feed '%s' not defined\n",
4020 filename
, line_num
, arg
);
4022 stream
->feed
= sfeed
;
4025 } else if (!strcasecmp(cmd
, "Format")) {
4026 get_arg(arg
, sizeof(arg
), &p
);
4027 if (!strcmp(arg
, "status")) {
4028 stream
->stream_type
= STREAM_TYPE_STATUS
;
4031 stream
->stream_type
= STREAM_TYPE_LIVE
;
4032 /* jpeg cannot be used here, so use single frame jpeg */
4033 if (!strcmp(arg
, "jpeg"))
4034 strcpy(arg
, "mjpeg");
4035 stream
->fmt
= guess_stream_format(arg
, NULL
, NULL
);
4037 fprintf(stderr
, "%s:%d: Unknown Format: %s\n",
4038 filename
, line_num
, arg
);
4043 audio_id
= stream
->fmt
->audio_codec
;
4044 video_id
= stream
->fmt
->video_codec
;
4046 } else if (!strcasecmp(cmd
, "InputFormat")) {
4047 get_arg(arg
, sizeof(arg
), &p
);
4048 stream
->ifmt
= av_find_input_format(arg
);
4049 if (!stream
->ifmt
) {
4050 fprintf(stderr
, "%s:%d: Unknown input format: %s\n",
4051 filename
, line_num
, arg
);
4053 } else if (!strcasecmp(cmd
, "FaviconURL")) {
4054 if (stream
&& stream
->stream_type
== STREAM_TYPE_STATUS
) {
4055 get_arg(stream
->feed_filename
, sizeof(stream
->feed_filename
), &p
);
4057 fprintf(stderr
, "%s:%d: FaviconURL only permitted for status streams\n",
4058 filename
, line_num
);
4061 } else if (!strcasecmp(cmd
, "Author")) {
4063 get_arg(stream
->author
, sizeof(stream
->author
), &p
);
4065 } else if (!strcasecmp(cmd
, "Comment")) {
4067 get_arg(stream
->comment
, sizeof(stream
->comment
), &p
);
4069 } else if (!strcasecmp(cmd
, "Copyright")) {
4071 get_arg(stream
->copyright
, sizeof(stream
->copyright
), &p
);
4073 } else if (!strcasecmp(cmd
, "Title")) {
4075 get_arg(stream
->title
, sizeof(stream
->title
), &p
);
4077 } else if (!strcasecmp(cmd
, "Preroll")) {
4078 get_arg(arg
, sizeof(arg
), &p
);
4080 stream
->prebuffer
= atof(arg
) * 1000;
4082 } else if (!strcasecmp(cmd
, "StartSendOnKey")) {
4084 stream
->send_on_key
= 1;
4086 } else if (!strcasecmp(cmd
, "AudioCodec")) {
4087 get_arg(arg
, sizeof(arg
), &p
);
4088 audio_id
= opt_audio_codec(arg
);
4089 if (audio_id
== CODEC_ID_NONE
) {
4090 fprintf(stderr
, "%s:%d: Unknown AudioCodec: %s\n",
4091 filename
, line_num
, arg
);
4094 } else if (!strcasecmp(cmd
, "VideoCodec")) {
4095 get_arg(arg
, sizeof(arg
), &p
);
4096 video_id
= opt_video_codec(arg
);
4097 if (video_id
== CODEC_ID_NONE
) {
4098 fprintf(stderr
, "%s:%d: Unknown VideoCodec: %s\n",
4099 filename
, line_num
, arg
);
4102 } else if (!strcasecmp(cmd
, "MaxTime")) {
4103 get_arg(arg
, sizeof(arg
), &p
);
4105 stream
->max_time
= atof(arg
) * 1000;
4107 } else if (!strcasecmp(cmd
, "AudioBitRate")) {
4108 get_arg(arg
, sizeof(arg
), &p
);
4110 audio_enc
.bit_rate
= atoi(arg
) * 1000;
4112 } else if (!strcasecmp(cmd
, "AudioChannels")) {
4113 get_arg(arg
, sizeof(arg
), &p
);
4115 audio_enc
.channels
= atoi(arg
);
4117 } else if (!strcasecmp(cmd
, "AudioSampleRate")) {
4118 get_arg(arg
, sizeof(arg
), &p
);
4120 audio_enc
.sample_rate
= atoi(arg
);
4122 } else if (!strcasecmp(cmd
, "AudioQuality")) {
4123 get_arg(arg
, sizeof(arg
), &p
);
4125 // audio_enc.quality = atof(arg) * 1000;
4127 } else if (!strcasecmp(cmd
, "VideoBitRateRange")) {
4129 int minrate
, maxrate
;
4131 get_arg(arg
, sizeof(arg
), &p
);
4133 if (sscanf(arg
, "%d-%d", &minrate
, &maxrate
) == 2) {
4134 video_enc
.rc_min_rate
= minrate
* 1000;
4135 video_enc
.rc_max_rate
= maxrate
* 1000;
4137 fprintf(stderr
, "%s:%d: Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n",
4138 filename
, line_num
, arg
);
4142 } else if (!strcasecmp(cmd
, "Debug")) {
4144 get_arg(arg
, sizeof(arg
), &p
);
4145 video_enc
.debug
= strtol(arg
,0,0);
4147 } else if (!strcasecmp(cmd
, "Strict")) {
4149 get_arg(arg
, sizeof(arg
), &p
);
4150 video_enc
.strict_std_compliance
= atoi(arg
);
4152 } else if (!strcasecmp(cmd
, "VideoBufferSize")) {
4154 get_arg(arg
, sizeof(arg
), &p
);
4155 video_enc
.rc_buffer_size
= atoi(arg
) * 8*1024;
4157 } else if (!strcasecmp(cmd
, "VideoBitRateTolerance")) {
4159 get_arg(arg
, sizeof(arg
), &p
);
4160 video_enc
.bit_rate_tolerance
= atoi(arg
) * 1000;
4162 } else if (!strcasecmp(cmd
, "VideoBitRate")) {
4163 get_arg(arg
, sizeof(arg
), &p
);
4165 video_enc
.bit_rate
= atoi(arg
) * 1000;
4167 } else if (!strcasecmp(cmd
, "VideoSize")) {
4168 get_arg(arg
, sizeof(arg
), &p
);
4170 av_parse_video_frame_size(&video_enc
.width
, &video_enc
.height
, arg
);
4171 if ((video_enc
.width
% 16) != 0 ||
4172 (video_enc
.height
% 16) != 0) {
4173 fprintf(stderr
, "%s:%d: Image size must be a multiple of 16\n",
4174 filename
, line_num
);
4178 } else if (!strcasecmp(cmd
, "VideoFrameRate")) {
4179 get_arg(arg
, sizeof(arg
), &p
);
4181 video_enc
.time_base
.num
= DEFAULT_FRAME_RATE_BASE
;
4182 video_enc
.time_base
.den
= (int)(strtod(arg
, NULL
) * video_enc
.time_base
.num
);
4184 } else if (!strcasecmp(cmd
, "VideoGopSize")) {
4185 get_arg(arg
, sizeof(arg
), &p
);
4187 video_enc
.gop_size
= atoi(arg
);
4189 } else if (!strcasecmp(cmd
, "VideoIntraOnly")) {
4191 video_enc
.gop_size
= 1;
4193 } else if (!strcasecmp(cmd
, "VideoHighQuality")) {
4195 video_enc
.mb_decision
= FF_MB_DECISION_BITS
;
4197 } else if (!strcasecmp(cmd
, "Video4MotionVector")) {
4199 video_enc
.mb_decision
= FF_MB_DECISION_BITS
; //FIXME remove
4200 video_enc
.flags
|= CODEC_FLAG_4MV
;
4202 } else if (!strcasecmp(cmd
, "VideoTag")) {
4203 get_arg(arg
, sizeof(arg
), &p
);
4204 if ((strlen(arg
) == 4) && stream
) {
4205 video_enc
.codec_tag
= ff_get_fourcc(arg
);
4207 } else if (!strcasecmp(cmd
, "BitExact")) {
4209 video_enc
.flags
|= CODEC_FLAG_BITEXACT
;
4211 } else if (!strcasecmp(cmd
, "DctFastint")) {
4213 video_enc
.dct_algo
= FF_DCT_FASTINT
;
4215 } else if (!strcasecmp(cmd
, "IdctSimple")) {
4217 video_enc
.idct_algo
= FF_IDCT_SIMPLE
;
4219 } else if (!strcasecmp(cmd
, "Qscale")) {
4220 get_arg(arg
, sizeof(arg
), &p
);
4222 video_enc
.flags
|= CODEC_FLAG_QSCALE
;
4223 video_enc
.global_quality
= FF_QP2LAMBDA
* atoi(arg
);
4225 } else if (!strcasecmp(cmd
, "VideoQDiff")) {
4226 get_arg(arg
, sizeof(arg
), &p
);
4228 video_enc
.max_qdiff
= atoi(arg
);
4229 if (video_enc
.max_qdiff
< 1 || video_enc
.max_qdiff
> 31) {
4230 fprintf(stderr
, "%s:%d: VideoQDiff out of range\n",
4231 filename
, line_num
);
4235 } else if (!strcasecmp(cmd
, "VideoQMax")) {
4236 get_arg(arg
, sizeof(arg
), &p
);
4238 video_enc
.qmax
= atoi(arg
);
4239 if (video_enc
.qmax
< 1 || video_enc
.qmax
> 31) {
4240 fprintf(stderr
, "%s:%d: VideoQMax out of range\n",
4241 filename
, line_num
);
4245 } else if (!strcasecmp(cmd
, "VideoQMin")) {
4246 get_arg(arg
, sizeof(arg
), &p
);
4248 video_enc
.qmin
= atoi(arg
);
4249 if (video_enc
.qmin
< 1 || video_enc
.qmin
> 31) {
4250 fprintf(stderr
, "%s:%d: VideoQMin out of range\n",
4251 filename
, line_num
);
4255 } else if (!strcasecmp(cmd
, "LumaElim")) {
4256 get_arg(arg
, sizeof(arg
), &p
);
4258 video_enc
.luma_elim_threshold
= atoi(arg
);
4260 } else if (!strcasecmp(cmd
, "ChromaElim")) {
4261 get_arg(arg
, sizeof(arg
), &p
);
4263 video_enc
.chroma_elim_threshold
= atoi(arg
);
4265 } else if (!strcasecmp(cmd
, "LumiMask")) {
4266 get_arg(arg
, sizeof(arg
), &p
);
4268 video_enc
.lumi_masking
= atof(arg
);
4270 } else if (!strcasecmp(cmd
, "DarkMask")) {
4271 get_arg(arg
, sizeof(arg
), &p
);
4273 video_enc
.dark_masking
= atof(arg
);
4275 } else if (!strcasecmp(cmd
, "NoVideo")) {
4276 video_id
= CODEC_ID_NONE
;
4277 } else if (!strcasecmp(cmd
, "NoAudio")) {
4278 audio_id
= CODEC_ID_NONE
;
4279 } else if (!strcasecmp(cmd
, "ACL")) {
4282 get_arg(arg
, sizeof(arg
), &p
);
4283 if (strcasecmp(arg
, "allow") == 0) {
4284 acl
.action
= IP_ALLOW
;
4285 } else if (strcasecmp(arg
, "deny") == 0) {
4286 acl
.action
= IP_DENY
;
4288 fprintf(stderr
, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
4289 filename
, line_num
, arg
);
4293 get_arg(arg
, sizeof(arg
), &p
);
4295 if (resolve_host(&acl
.first
, arg
) != 0) {
4296 fprintf(stderr
, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4297 filename
, line_num
, arg
);
4300 acl
.last
= acl
.first
;
4303 get_arg(arg
, sizeof(arg
), &p
);
4306 if (resolve_host(&acl
.last
, arg
) != 0) {
4307 fprintf(stderr
, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4308 filename
, line_num
, arg
);
4314 IPAddressACL
*nacl
= (IPAddressACL
*) av_mallocz(sizeof(*nacl
));
4315 IPAddressACL
**naclp
= 0;
4321 naclp
= &stream
->acl
;
4325 fprintf(stderr
, "%s:%d: ACL found not in <stream> or <feed>\n",
4326 filename
, line_num
);
4332 naclp
= &(*naclp
)->next
;
4337 } else if (!strcasecmp(cmd
, "RTSPOption")) {
4338 get_arg(arg
, sizeof(arg
), &p
);
4340 av_freep(&stream
->rtsp_option
);
4341 stream
->rtsp_option
= av_strdup(arg
);
4343 } else if (!strcasecmp(cmd
, "MulticastAddress")) {
4344 get_arg(arg
, sizeof(arg
), &p
);
4346 if (resolve_host(&stream
->multicast_ip
, arg
) != 0) {
4347 fprintf(stderr
, "%s:%d: Invalid host/IP address: %s\n",
4348 filename
, line_num
, arg
);
4351 stream
->is_multicast
= 1;
4352 stream
->loop
= 1; /* default is looping */
4354 } else if (!strcasecmp(cmd
, "MulticastPort")) {
4355 get_arg(arg
, sizeof(arg
), &p
);
4357 stream
->multicast_port
= atoi(arg
);
4359 } else if (!strcasecmp(cmd
, "MulticastTTL")) {
4360 get_arg(arg
, sizeof(arg
), &p
);
4362 stream
->multicast_ttl
= atoi(arg
);
4364 } else if (!strcasecmp(cmd
, "NoLoop")) {
4368 } else if (!strcasecmp(cmd
, "</Stream>")) {
4370 fprintf(stderr
, "%s:%d: No corresponding <Stream> for </Stream>\n",
4371 filename
, line_num
);
4374 if (stream
->feed
&& stream
->fmt
&& strcmp(stream
->fmt
->name
, "ffm") != 0) {
4375 if (audio_id
!= CODEC_ID_NONE
) {
4376 audio_enc
.codec_type
= CODEC_TYPE_AUDIO
;
4377 audio_enc
.codec_id
= audio_id
;
4378 add_codec(stream
, &audio_enc
);
4380 if (video_id
!= CODEC_ID_NONE
) {
4381 video_enc
.codec_type
= CODEC_TYPE_VIDEO
;
4382 video_enc
.codec_id
= video_id
;
4383 add_codec(stream
, &video_enc
);
4387 } else if (!strcasecmp(cmd
, "<Redirect")) {
4388 /*********************************************/
4390 if (stream
|| feed
|| redirect
) {
4391 fprintf(stderr
, "%s:%d: Already in a tag\n",
4392 filename
, line_num
);
4395 redirect
= av_mallocz(sizeof(FFStream
));
4396 *last_stream
= redirect
;
4397 last_stream
= &redirect
->next
;
4399 get_arg(redirect
->filename
, sizeof(redirect
->filename
), &p
);
4400 q
= strrchr(redirect
->filename
, '>');
4403 redirect
->stream_type
= STREAM_TYPE_REDIRECT
;
4405 } else if (!strcasecmp(cmd
, "URL")) {
4407 get_arg(redirect
->feed_filename
, sizeof(redirect
->feed_filename
), &p
);
4409 } else if (!strcasecmp(cmd
, "</Redirect>")) {
4411 fprintf(stderr
, "%s:%d: No corresponding <Redirect> for </Redirect>\n",
4412 filename
, line_num
);
4415 if (!redirect
->feed_filename
[0]) {
4416 fprintf(stderr
, "%s:%d: No URL found for <Redirect>\n",
4417 filename
, line_num
);
4421 } else if (!strcasecmp(cmd
, "LoadModule")) {
4422 get_arg(arg
, sizeof(arg
), &p
);
4426 fprintf(stderr
, "%s:%d: Module support not compiled into this version: '%s'\n",
4427 filename
, line_num
, arg
);
4431 fprintf(stderr
, "%s:%d: Incorrect keyword: '%s'\n",
4432 filename
, line_num
, cmd
);
4444 static void show_banner(void)
4446 printf("ffserver version " FFMPEG_VERSION
", Copyright (c) 2000-2006 Fabrice Bellard, et al.\n");
4449 static void show_help(void)
4452 printf("usage: ffserver [-L] [-h] [-f configfile]\n"
4453 "Hyper fast multi format Audio/Video streaming server\n"
4455 "-L : print the LICENSE\n"
4457 "-f configfile : use configfile instead of /etc/ffserver.conf\n"
4461 static void show_license(void)
4465 "FFmpeg is free software; you can redistribute it and/or\n"
4466 "modify it under the terms of the GNU Lesser General Public\n"
4467 "License as published by the Free Software Foundation; either\n"
4468 "version 2.1 of the License, or (at your option) any later version.\n"
4470 "FFmpeg is distributed in the hope that it will be useful,\n"
4471 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
4472 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
4473 "Lesser General Public License for more details.\n"
4475 "You should have received a copy of the GNU Lesser General Public\n"
4476 "License along with FFmpeg; if not, write to the Free Software\n"
4477 "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
4481 static void handle_child_exit(int sig
)
4486 while ((pid
= waitpid(-1, &status
, WNOHANG
)) > 0) {
4489 for (feed
= first_feed
; feed
; feed
= feed
->next
) {
4490 if (feed
->pid
== pid
) {
4491 int uptime
= time(0) - feed
->pid_start
;
4494 fprintf(stderr
, "%s: Pid %d exited with status %d after %d seconds\n", feed
->filename
, pid
, status
, uptime
);
4497 /* Turn off any more restarts */
4498 feed
->child_argv
= 0;
4504 need_to_start_children
= 1;
4507 int main(int argc
, char **argv
)
4509 const char *config_filename
;
4511 struct sigaction sigact
;
4515 config_filename
= "/etc/ffserver.conf";
4517 my_program_name
= argv
[0];
4518 my_program_dir
= getcwd(0, 0);
4519 ffserver_daemon
= 1;
4522 c
= getopt(argc
, argv
, "ndLh?f:");
4538 ffserver_daemon
= 0;
4541 config_filename
= optarg
;
4548 putenv("http_proxy"); /* Kill the http_proxy */
4550 av_init_random(av_gettime() + (getpid() << 16), &random_state
);
4552 /* address on which the server will handle HTTP connections */
4553 my_http_addr
.sin_family
= AF_INET
;
4554 my_http_addr
.sin_port
= htons (8080);
4555 my_http_addr
.sin_addr
.s_addr
= htonl (INADDR_ANY
);
4557 /* address on which the server will handle RTSP connections */
4558 my_rtsp_addr
.sin_family
= AF_INET
;
4559 my_rtsp_addr
.sin_port
= htons (5454);
4560 my_rtsp_addr
.sin_addr
.s_addr
= htonl (INADDR_ANY
);
4562 nb_max_connections
= 5;
4563 max_bandwidth
= 1000;
4564 first_stream
= NULL
;
4565 logfilename
[0] = '\0';
4567 memset(&sigact
, 0, sizeof(sigact
));
4568 sigact
.sa_handler
= handle_child_exit
;
4569 sigact
.sa_flags
= SA_NOCLDSTOP
| SA_RESTART
;
4570 sigaction(SIGCHLD
, &sigact
, 0);
4572 if (parse_ffconfig(config_filename
) < 0) {
4573 fprintf(stderr
, "Incorrect config file - exiting.\n");
4577 build_file_streams();
4579 build_feed_streams();
4581 compute_bandwidth();
4583 /* put the process in background and detach it from its TTY */
4584 if (ffserver_daemon
) {
4591 } else if (pid
> 0) {
4599 open("/dev/null", O_RDWR
);
4600 if (strcmp(logfilename
, "-") != 0) {
4610 signal(SIGPIPE
, SIG_IGN
);
4612 /* open log file if needed */
4613 if (logfilename
[0] != '\0') {
4614 if (!strcmp(logfilename
, "-"))
4617 logfile
= fopen(logfilename
, "w");
4620 if (http_server() < 0) {
4621 fprintf(stderr
, "Could not start server\n");