2 * Multiple format streaming server
3 * Copyright (c) 2000, 2001, 2002 Fabrice Bellard
5 * This file is part of FFmpeg.
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 #define _XOPEN_SOURCE 600
25 #ifndef HAVE_CLOSESOCKET
26 #define closesocket close
31 #include "libavutil/random.h"
32 #include "libavutil/avstring.h"
33 #include "libavformat/avformat.h"
34 #include "libavformat/network.h"
35 #include "libavformat/os_support.h"
36 #include "libavformat/rtp.h"
37 #include "libavformat/rtsp.h"
38 #include "libavcodec/opt.h"
42 #include <sys/ioctl.h>
48 #undef time //needed because HAVE_AV_CONFIG_H is defined on top
60 const char program_name
[] = "FFserver";
61 const int program_birth_year
= 2000;
63 static const OptionDef options
[];
65 /* maximum number of simultaneous HTTP connections */
66 #define HTTP_MAX_CONNECTIONS 2000
69 HTTPSTATE_WAIT_REQUEST
,
70 HTTPSTATE_SEND_HEADER
,
71 HTTPSTATE_SEND_DATA_HEADER
,
72 HTTPSTATE_SEND_DATA
, /* sending TCP or UDP data */
73 HTTPSTATE_SEND_DATA_TRAILER
,
74 HTTPSTATE_RECEIVE_DATA
,
75 HTTPSTATE_WAIT_FEED
, /* wait for data from the feed */
78 RTSPSTATE_WAIT_REQUEST
,
80 RTSPSTATE_SEND_PACKET
,
83 static const char *http_state
[] = {
99 #define IOBUFFER_INIT_SIZE 8192
101 /* timeouts are in ms */
102 #define HTTP_REQUEST_TIMEOUT (15 * 1000)
103 #define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
105 #define SYNC_TIMEOUT (10 * 1000)
108 int64_t count1
, count2
;
109 int64_t time1
, time2
;
112 /* context associated with one connection */
113 typedef struct HTTPContext
{
114 enum HTTPState state
;
115 int fd
; /* socket file descriptor */
116 struct sockaddr_in from_addr
; /* origin */
117 struct pollfd
*poll_entry
; /* used when polling */
119 uint8_t *buffer_ptr
, *buffer_end
;
122 struct HTTPContext
*next
;
123 int got_key_frame
; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
127 /* input format handling */
128 AVFormatContext
*fmt_in
;
129 int64_t start_time
; /* In milliseconds - this wraps fairly often */
130 int64_t first_pts
; /* initial pts value */
131 int64_t cur_pts
; /* current pts value from the stream in us */
132 int64_t cur_frame_duration
; /* duration of the current frame in us */
133 int cur_frame_bytes
; /* output frame size, needed to compute
134 the time at which we send each
136 int pts_stream_index
; /* stream we choose as clock reference */
137 int64_t cur_clock
; /* current clock reference value in us */
138 /* output format handling */
139 struct FFStream
*stream
;
140 /* -1 is invalid stream */
141 int feed_streams
[MAX_STREAMS
]; /* index of streams in the feed */
142 int switch_feed_streams
[MAX_STREAMS
]; /* index of streams in the feed */
144 AVFormatContext fmt_ctx
; /* instance of FFStream for one user */
145 int last_packet_sent
; /* true if last data packet was sent */
147 DataRateData datarate
;
154 int is_packetized
; /* if true, the stream is packetized */
155 int packet_stream_index
; /* current stream for output in state machine */
157 /* RTSP state specific */
158 uint8_t *pb_buffer
; /* XXX: use that in all the code */
160 int seq
; /* RTSP sequence number */
162 /* RTP state specific */
163 enum RTSPProtocol rtp_protocol
;
164 char session_id
[32]; /* session id */
165 AVFormatContext
*rtp_ctx
[MAX_STREAMS
];
167 /* RTP/UDP specific */
168 URLContext
*rtp_handles
[MAX_STREAMS
];
170 /* RTP/TCP specific */
171 struct HTTPContext
*rtsp_c
;
172 uint8_t *packet_buffer
, *packet_buffer_ptr
, *packet_buffer_end
;
175 /* each generated stream is described here */
179 STREAM_TYPE_REDIRECT
,
182 enum IPAddressAction
{
187 typedef struct IPAddressACL
{
188 struct IPAddressACL
*next
;
189 enum IPAddressAction action
;
190 /* These are in host order */
191 struct in_addr first
;
195 /* description of each stream of the ffserver.conf file */
196 typedef struct FFStream
{
197 enum StreamType stream_type
;
198 char filename
[1024]; /* stream filename */
199 struct FFStream
*feed
; /* feed we are using (can be null if
201 AVFormatParameters
*ap_in
; /* input parameters */
202 AVInputFormat
*ifmt
; /* if non NULL, force input format */
206 int prebuffer
; /* Number of millseconds early to start */
207 int64_t max_time
; /* Number of milliseconds to run */
209 AVStream
*streams
[MAX_STREAMS
];
210 int feed_streams
[MAX_STREAMS
]; /* index of streams in the feed */
211 char feed_filename
[1024]; /* file name of the feed storage, or
212 input file name for a stream */
217 pid_t pid
; /* Of ffmpeg process */
218 time_t pid_start
; /* Of ffmpeg process */
220 struct FFStream
*next
;
221 unsigned bandwidth
; /* bandwidth, in kbits/s */
224 /* multicast specific */
226 struct in_addr multicast_ip
;
227 int multicast_port
; /* first port used for multicast */
229 int loop
; /* if true, send the stream in loops (only meaningful if file) */
232 int feed_opened
; /* true if someone is writing to the feed */
233 int is_feed
; /* true if it is a feed */
234 int readonly
; /* True if writing is prohibited to the file */
236 int64_t bytes_served
;
237 int64_t feed_max_size
; /* maximum storage size, zero means unlimited */
238 int64_t feed_write_index
; /* current write position in feed (it wraps around) */
239 int64_t feed_size
; /* current size of feed */
240 struct FFStream
*next_feed
;
243 typedef struct FeedData
{
244 long long data_count
;
245 float avg_frame_size
; /* frame size averaged over last frames with exponential mean */
248 static struct sockaddr_in my_http_addr
;
249 static struct sockaddr_in my_rtsp_addr
;
251 static char logfilename
[1024];
252 static HTTPContext
*first_http_ctx
;
253 static FFStream
*first_feed
; /* contains only feeds */
254 static FFStream
*first_stream
; /* contains all streams, including feeds */
256 static void new_connection(int server_fd
, int is_rtsp
);
257 static void close_connection(HTTPContext
*c
);
260 static int handle_connection(HTTPContext
*c
);
261 static int http_parse_request(HTTPContext
*c
);
262 static int http_send_data(HTTPContext
*c
);
263 static void compute_status(HTTPContext
*c
);
264 static int open_input_stream(HTTPContext
*c
, const char *info
);
265 static int http_start_receive_data(HTTPContext
*c
);
266 static int http_receive_data(HTTPContext
*c
);
269 static int rtsp_parse_request(HTTPContext
*c
);
270 static void rtsp_cmd_describe(HTTPContext
*c
, const char *url
);
271 static void rtsp_cmd_options(HTTPContext
*c
, const char *url
);
272 static void rtsp_cmd_setup(HTTPContext
*c
, const char *url
, RTSPHeader
*h
);
273 static void rtsp_cmd_play(HTTPContext
*c
, const char *url
, RTSPHeader
*h
);
274 static void rtsp_cmd_pause(HTTPContext
*c
, const char *url
, RTSPHeader
*h
);
275 static void rtsp_cmd_teardown(HTTPContext
*c
, const char *url
, RTSPHeader
*h
);
278 static int prepare_sdp_description(FFStream
*stream
, uint8_t **pbuffer
,
279 struct in_addr my_ip
);
282 static HTTPContext
*rtp_new_connection(struct sockaddr_in
*from_addr
,
283 FFStream
*stream
, const char *session_id
,
284 enum RTSPProtocol rtp_protocol
);
285 static int rtp_new_av_stream(HTTPContext
*c
,
286 int stream_index
, struct sockaddr_in
*dest_addr
,
287 HTTPContext
*rtsp_c
);
289 static const char *my_program_name
;
290 static const char *my_program_dir
;
292 static const char *config_filename
;
293 static int ffserver_debug
;
294 static int ffserver_daemon
;
295 static int no_launch
;
296 static int need_to_start_children
;
298 static int nb_max_connections
= 5;
299 static int nb_connections
;
301 static uint64_t max_bandwidth
= 1000;
302 static uint64_t current_bandwidth
;
304 static int64_t cur_time
; // Making this global saves on passing it around everywhere
306 static AVRandomState random_state
;
308 static FILE *logfile
= NULL
;
310 static char *ctime1(char *buf2
)
318 p
= buf2
+ strlen(p
) - 1;
324 static void http_vlog(const char *fmt
, va_list vargs
)
326 static int print_prefix
= 1;
331 fprintf(logfile
, "%s ", buf
);
333 print_prefix
= strstr(fmt
, "\n") != NULL
;
334 vfprintf(logfile
, fmt
, vargs
);
339 void __attribute__ ((format (printf
, 1, 2))) http_log(const char *fmt
, ...)
342 va_start(vargs
, fmt
);
343 http_vlog(fmt
, vargs
);
347 static void http_av_log(void *ptr
, int level
, const char *fmt
, va_list vargs
)
349 static int print_prefix
= 1;
350 AVClass
*avc
= ptr
? *(AVClass
**)ptr
: NULL
;
351 if (level
> av_log_level
)
353 if (print_prefix
&& avc
)
354 http_log("[%s @ %p]", avc
->item_name(ptr
), ptr
);
355 print_prefix
= strstr(fmt
, "\n") != NULL
;
356 http_vlog(fmt
, vargs
);
359 static void log_connection(HTTPContext
*c
)
364 http_log("%s - - [%s] \"%s %s\" %d %"PRId64
"\n",
365 inet_ntoa(c
->from_addr
.sin_addr
), c
->method
, c
->url
,
366 c
->protocol
, (c
->http_error
? c
->http_error
: 200), c
->data_count
);
369 static void update_datarate(DataRateData
*drd
, int64_t count
)
371 if (!drd
->time1
&& !drd
->count1
) {
372 drd
->time1
= drd
->time2
= cur_time
;
373 drd
->count1
= drd
->count2
= count
;
374 } else if (cur_time
- drd
->time2
> 5000) {
375 drd
->time1
= drd
->time2
;
376 drd
->count1
= drd
->count2
;
377 drd
->time2
= cur_time
;
382 /* In bytes per second */
383 static int compute_datarate(DataRateData
*drd
, int64_t count
)
385 if (cur_time
== drd
->time1
)
388 return ((count
- drd
->count1
) * 1000) / (cur_time
- drd
->time1
);
392 static void start_children(FFStream
*feed
)
397 for (; feed
; feed
= feed
->next
) {
398 if (feed
->child_argv
&& !feed
->pid
) {
399 feed
->pid_start
= time(0);
404 http_log("Unable to create children\n");
413 av_strlcpy(pathname
, my_program_name
, sizeof(pathname
));
415 slash
= strrchr(pathname
, '/');
420 strcpy(slash
, "ffmpeg");
422 http_log("Launch commandline: ");
423 http_log("%s ", pathname
);
424 for (i
= 1; feed
->child_argv
[i
] && feed
->child_argv
[i
][0]; i
++)
425 http_log("%s ", feed
->child_argv
[i
]);
428 for (i
= 3; i
< 256; i
++)
431 if (!ffserver_debug
) {
432 i
= open("/dev/null", O_RDWR
);
441 /* This is needed to make relative pathnames work */
442 chdir(my_program_dir
);
444 signal(SIGPIPE
, SIG_DFL
);
446 execvp(pathname
, feed
->child_argv
);
454 /* open a listening socket */
455 static int socket_open_listen(struct sockaddr_in
*my_addr
)
459 server_fd
= socket(AF_INET
,SOCK_STREAM
,0);
466 setsockopt(server_fd
, SOL_SOCKET
, SO_REUSEADDR
, &tmp
, sizeof(tmp
));
468 if (bind (server_fd
, (struct sockaddr
*) my_addr
, sizeof (*my_addr
)) < 0) {
470 snprintf(bindmsg
, sizeof(bindmsg
), "bind(port %d)", ntohs(my_addr
->sin_port
));
472 closesocket(server_fd
);
476 if (listen (server_fd
, 5) < 0) {
478 closesocket(server_fd
);
481 ff_socket_nonblock(server_fd
, 1);
486 /* start all multicast streams */
487 static void start_multicast(void)
492 struct sockaddr_in dest_addr
;
493 int default_port
, stream_index
;
496 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
497 if (stream
->is_multicast
) {
498 /* open the RTP connection */
499 snprintf(session_id
, sizeof(session_id
), "%08x%08x",
500 av_random(&random_state
), av_random(&random_state
));
502 /* choose a port if none given */
503 if (stream
->multicast_port
== 0) {
504 stream
->multicast_port
= default_port
;
508 dest_addr
.sin_family
= AF_INET
;
509 dest_addr
.sin_addr
= stream
->multicast_ip
;
510 dest_addr
.sin_port
= htons(stream
->multicast_port
);
512 rtp_c
= rtp_new_connection(&dest_addr
, stream
, session_id
,
513 RTSP_PROTOCOL_RTP_UDP_MULTICAST
);
517 if (open_input_stream(rtp_c
, "") < 0) {
518 http_log("Could not open input stream for stream '%s'\n",
523 /* open each RTP stream */
524 for(stream_index
= 0; stream_index
< stream
->nb_streams
;
526 dest_addr
.sin_port
= htons(stream
->multicast_port
+
528 if (rtp_new_av_stream(rtp_c
, stream_index
, &dest_addr
, NULL
) < 0) {
529 http_log("Could not open output stream '%s/streamid=%d'\n",
530 stream
->filename
, stream_index
);
535 /* change state to send data */
536 rtp_c
->state
= HTTPSTATE_SEND_DATA
;
541 /* main loop of the http server */
542 static int http_server(void)
544 int server_fd
= 0, rtsp_server_fd
= 0;
545 int ret
, delay
, delay1
;
546 struct pollfd poll_table
[HTTP_MAX_CONNECTIONS
+ 2], *poll_entry
;
547 HTTPContext
*c
, *c_next
;
549 if (my_http_addr
.sin_port
) {
550 server_fd
= socket_open_listen(&my_http_addr
);
555 if (my_rtsp_addr
.sin_port
) {
556 rtsp_server_fd
= socket_open_listen(&my_rtsp_addr
);
557 if (rtsp_server_fd
< 0)
561 if (!rtsp_server_fd
&& !server_fd
) {
562 http_log("HTTP and RTSP disabled.\n");
566 http_log("ffserver started.\n");
568 start_children(first_feed
);
573 poll_entry
= poll_table
;
575 poll_entry
->fd
= server_fd
;
576 poll_entry
->events
= POLLIN
;
579 if (rtsp_server_fd
) {
580 poll_entry
->fd
= rtsp_server_fd
;
581 poll_entry
->events
= POLLIN
;
585 /* wait for events on each HTTP handle */
592 case HTTPSTATE_SEND_HEADER
:
593 case RTSPSTATE_SEND_REPLY
:
594 case RTSPSTATE_SEND_PACKET
:
595 c
->poll_entry
= poll_entry
;
597 poll_entry
->events
= POLLOUT
;
600 case HTTPSTATE_SEND_DATA_HEADER
:
601 case HTTPSTATE_SEND_DATA
:
602 case HTTPSTATE_SEND_DATA_TRAILER
:
603 if (!c
->is_packetized
) {
604 /* for TCP, we output as much as we can (may need to put a limit) */
605 c
->poll_entry
= poll_entry
;
607 poll_entry
->events
= POLLOUT
;
610 /* when ffserver is doing the timing, we work by
611 looking at which packet need to be sent every
613 delay1
= 10; /* one tick wait XXX: 10 ms assumed */
618 case HTTPSTATE_WAIT_REQUEST
:
619 case HTTPSTATE_RECEIVE_DATA
:
620 case HTTPSTATE_WAIT_FEED
:
621 case RTSPSTATE_WAIT_REQUEST
:
622 /* need to catch errors */
623 c
->poll_entry
= poll_entry
;
625 poll_entry
->events
= POLLIN
;/* Maybe this will work */
629 c
->poll_entry
= NULL
;
635 /* wait for an event on one connection. We poll at least every
636 second to handle timeouts */
638 ret
= poll(poll_table
, poll_entry
- poll_table
, delay
);
639 if (ret
< 0 && ff_neterrno() != FF_NETERROR(EAGAIN
) &&
640 ff_neterrno() != FF_NETERROR(EINTR
))
644 cur_time
= av_gettime() / 1000;
646 if (need_to_start_children
) {
647 need_to_start_children
= 0;
648 start_children(first_feed
);
651 /* now handle the events */
652 for(c
= first_http_ctx
; c
!= NULL
; c
= c_next
) {
654 if (handle_connection(c
) < 0) {
655 /* close and free the connection */
661 poll_entry
= poll_table
;
663 /* new HTTP connection request ? */
664 if (poll_entry
->revents
& POLLIN
)
665 new_connection(server_fd
, 0);
668 if (rtsp_server_fd
) {
669 /* new RTSP connection request ? */
670 if (poll_entry
->revents
& POLLIN
)
671 new_connection(rtsp_server_fd
, 1);
676 /* start waiting for a new HTTP/RTSP request */
677 static void start_wait_request(HTTPContext
*c
, int is_rtsp
)
679 c
->buffer_ptr
= c
->buffer
;
680 c
->buffer_end
= c
->buffer
+ c
->buffer_size
- 1; /* leave room for '\0' */
683 c
->timeout
= cur_time
+ RTSP_REQUEST_TIMEOUT
;
684 c
->state
= RTSPSTATE_WAIT_REQUEST
;
686 c
->timeout
= cur_time
+ HTTP_REQUEST_TIMEOUT
;
687 c
->state
= HTTPSTATE_WAIT_REQUEST
;
691 static void new_connection(int server_fd
, int is_rtsp
)
693 struct sockaddr_in from_addr
;
695 HTTPContext
*c
= NULL
;
697 len
= sizeof(from_addr
);
698 fd
= accept(server_fd
, (struct sockaddr
*)&from_addr
,
701 http_log("error during accept %s\n", strerror(errno
));
704 ff_socket_nonblock(fd
, 1);
706 /* XXX: should output a warning page when coming
707 close to the connection limit */
708 if (nb_connections
>= nb_max_connections
)
711 /* add a new connection */
712 c
= av_mallocz(sizeof(HTTPContext
));
717 c
->poll_entry
= NULL
;
718 c
->from_addr
= from_addr
;
719 c
->buffer_size
= IOBUFFER_INIT_SIZE
;
720 c
->buffer
= av_malloc(c
->buffer_size
);
724 c
->next
= first_http_ctx
;
728 start_wait_request(c
, is_rtsp
);
740 static void close_connection(HTTPContext
*c
)
742 HTTPContext
**cp
, *c1
;
744 AVFormatContext
*ctx
;
748 /* remove connection from list */
749 cp
= &first_http_ctx
;
750 while ((*cp
) != NULL
) {
758 /* remove references, if any (XXX: do it faster) */
759 for(c1
= first_http_ctx
; c1
!= NULL
; c1
= c1
->next
) {
764 /* remove connection associated resources */
768 /* close each frame parser */
769 for(i
=0;i
<c
->fmt_in
->nb_streams
;i
++) {
770 st
= c
->fmt_in
->streams
[i
];
771 if (st
->codec
->codec
)
772 avcodec_close(st
->codec
);
774 av_close_input_file(c
->fmt_in
);
777 /* free RTP output streams if any */
780 nb_streams
= c
->stream
->nb_streams
;
782 for(i
=0;i
<nb_streams
;i
++) {
785 av_write_trailer(ctx
);
788 h
= c
->rtp_handles
[i
];
795 if (!c
->last_packet_sent
) {
798 if (url_open_dyn_buf(&ctx
->pb
) >= 0) {
799 av_write_trailer(ctx
);
800 av_freep(&c
->pb_buffer
);
801 url_close_dyn_buf(ctx
->pb
, &c
->pb_buffer
);
806 for(i
=0; i
<ctx
->nb_streams
; i
++)
807 av_free(ctx
->streams
[i
]);
809 if (c
->stream
&& !c
->post
&& c
->stream
->stream_type
== STREAM_TYPE_LIVE
)
810 current_bandwidth
-= c
->stream
->bandwidth
;
812 /* signal that there is no feed if we are the feeder socket */
813 if (c
->state
== HTTPSTATE_RECEIVE_DATA
&& c
->stream
) {
814 c
->stream
->feed_opened
= 0;
818 av_freep(&c
->pb_buffer
);
819 av_freep(&c
->packet_buffer
);
825 static int handle_connection(HTTPContext
*c
)
830 case HTTPSTATE_WAIT_REQUEST
:
831 case RTSPSTATE_WAIT_REQUEST
:
833 if ((c
->timeout
- cur_time
) < 0)
835 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
))
838 /* no need to read if no events */
839 if (!(c
->poll_entry
->revents
& POLLIN
))
843 len
= recv(c
->fd
, c
->buffer_ptr
, 1, 0);
845 if (ff_neterrno() != FF_NETERROR(EAGAIN
) &&
846 ff_neterrno() != FF_NETERROR(EINTR
))
848 } else if (len
== 0) {
851 /* search for end of request. */
853 c
->buffer_ptr
+= len
;
855 if ((ptr
>= c
->buffer
+ 2 && !memcmp(ptr
-2, "\n\n", 2)) ||
856 (ptr
>= c
->buffer
+ 4 && !memcmp(ptr
-4, "\r\n\r\n", 4))) {
857 /* request found : parse it and reply */
858 if (c
->state
== HTTPSTATE_WAIT_REQUEST
) {
859 ret
= http_parse_request(c
);
861 ret
= rtsp_parse_request(c
);
865 } else if (ptr
>= c
->buffer_end
) {
866 /* request too long: cannot do anything */
868 } else goto read_loop
;
872 case HTTPSTATE_SEND_HEADER
:
873 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
))
876 /* no need to write if no events */
877 if (!(c
->poll_entry
->revents
& POLLOUT
))
879 len
= send(c
->fd
, c
->buffer_ptr
, c
->buffer_end
- c
->buffer_ptr
, 0);
881 if (ff_neterrno() != FF_NETERROR(EAGAIN
) &&
882 ff_neterrno() != FF_NETERROR(EINTR
)) {
883 /* error : close connection */
884 av_freep(&c
->pb_buffer
);
888 c
->buffer_ptr
+= len
;
890 c
->stream
->bytes_served
+= len
;
891 c
->data_count
+= len
;
892 if (c
->buffer_ptr
>= c
->buffer_end
) {
893 av_freep(&c
->pb_buffer
);
897 /* all the buffer was sent : synchronize to the incoming stream */
898 c
->state
= HTTPSTATE_SEND_DATA_HEADER
;
899 c
->buffer_ptr
= c
->buffer_end
= c
->buffer
;
904 case HTTPSTATE_SEND_DATA
:
905 case HTTPSTATE_SEND_DATA_HEADER
:
906 case HTTPSTATE_SEND_DATA_TRAILER
:
907 /* for packetized output, we consider we can always write (the
908 input streams sets the speed). It may be better to verify
909 that we do not rely too much on the kernel queues */
910 if (!c
->is_packetized
) {
911 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
))
914 /* no need to read if no events */
915 if (!(c
->poll_entry
->revents
& POLLOUT
))
918 if (http_send_data(c
) < 0)
920 /* close connection if trailer sent */
921 if (c
->state
== HTTPSTATE_SEND_DATA_TRAILER
)
924 case HTTPSTATE_RECEIVE_DATA
:
925 /* no need to read if no events */
926 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
))
928 if (!(c
->poll_entry
->revents
& POLLIN
))
930 if (http_receive_data(c
) < 0)
933 case HTTPSTATE_WAIT_FEED
:
934 /* no need to read if no events */
935 if (c
->poll_entry
->revents
& (POLLIN
| POLLERR
| POLLHUP
))
938 /* nothing to do, we'll be waken up by incoming feed packets */
941 case RTSPSTATE_SEND_REPLY
:
942 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
)) {
943 av_freep(&c
->pb_buffer
);
946 /* no need to write if no events */
947 if (!(c
->poll_entry
->revents
& POLLOUT
))
949 len
= send(c
->fd
, c
->buffer_ptr
, c
->buffer_end
- c
->buffer_ptr
, 0);
951 if (ff_neterrno() != FF_NETERROR(EAGAIN
) &&
952 ff_neterrno() != FF_NETERROR(EINTR
)) {
953 /* error : close connection */
954 av_freep(&c
->pb_buffer
);
958 c
->buffer_ptr
+= len
;
959 c
->data_count
+= len
;
960 if (c
->buffer_ptr
>= c
->buffer_end
) {
961 /* all the buffer was sent : wait for a new request */
962 av_freep(&c
->pb_buffer
);
963 start_wait_request(c
, 1);
967 case RTSPSTATE_SEND_PACKET
:
968 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
)) {
969 av_freep(&c
->packet_buffer
);
972 /* no need to write if no events */
973 if (!(c
->poll_entry
->revents
& POLLOUT
))
975 len
= send(c
->fd
, c
->packet_buffer_ptr
,
976 c
->packet_buffer_end
- c
->packet_buffer_ptr
, 0);
978 if (ff_neterrno() != FF_NETERROR(EAGAIN
) &&
979 ff_neterrno() != FF_NETERROR(EINTR
)) {
980 /* error : close connection */
981 av_freep(&c
->packet_buffer
);
985 c
->packet_buffer_ptr
+= len
;
986 if (c
->packet_buffer_ptr
>= c
->packet_buffer_end
) {
987 /* all the buffer was sent : wait for a new request */
988 av_freep(&c
->packet_buffer
);
989 c
->state
= RTSPSTATE_WAIT_REQUEST
;
993 case HTTPSTATE_READY
:
1002 static int extract_rates(char *rates
, int ratelen
, const char *request
)
1006 for (p
= request
; *p
&& *p
!= '\r' && *p
!= '\n'; ) {
1007 if (strncasecmp(p
, "Pragma:", 7) == 0) {
1008 const char *q
= p
+ 7;
1010 while (*q
&& *q
!= '\n' && isspace(*q
))
1013 if (strncasecmp(q
, "stream-switch-entry=", 20) == 0) {
1019 memset(rates
, 0xff, ratelen
);
1022 while (*q
&& *q
!= '\n' && *q
!= ':')
1025 if (sscanf(q
, ":%d:%d", &stream_no
, &rate_no
) != 2)
1029 if (stream_no
< ratelen
&& stream_no
>= 0)
1030 rates
[stream_no
] = rate_no
;
1032 while (*q
&& *q
!= '\n' && !isspace(*q
))
1039 p
= strchr(p
, '\n');
1049 static int find_stream_in_feed(FFStream
*feed
, AVCodecContext
*codec
, int bit_rate
)
1052 int best_bitrate
= 100000000;
1055 for (i
= 0; i
< feed
->nb_streams
; i
++) {
1056 AVCodecContext
*feed_codec
= feed
->streams
[i
]->codec
;
1058 if (feed_codec
->codec_id
!= codec
->codec_id
||
1059 feed_codec
->sample_rate
!= codec
->sample_rate
||
1060 feed_codec
->width
!= codec
->width
||
1061 feed_codec
->height
!= codec
->height
)
1064 /* Potential stream */
1066 /* We want the fastest stream less than bit_rate, or the slowest
1067 * faster than bit_rate
1070 if (feed_codec
->bit_rate
<= bit_rate
) {
1071 if (best_bitrate
> bit_rate
|| feed_codec
->bit_rate
> best_bitrate
) {
1072 best_bitrate
= feed_codec
->bit_rate
;
1076 if (feed_codec
->bit_rate
< best_bitrate
) {
1077 best_bitrate
= feed_codec
->bit_rate
;
1086 static int modify_current_stream(HTTPContext
*c
, char *rates
)
1089 FFStream
*req
= c
->stream
;
1090 int action_required
= 0;
1092 /* Not much we can do for a feed */
1096 for (i
= 0; i
< req
->nb_streams
; i
++) {
1097 AVCodecContext
*codec
= req
->streams
[i
]->codec
;
1101 c
->switch_feed_streams
[i
] = req
->feed_streams
[i
];
1104 c
->switch_feed_streams
[i
] = find_stream_in_feed(req
->feed
, codec
, codec
->bit_rate
/ 2);
1107 /* Wants off or slow */
1108 c
->switch_feed_streams
[i
] = find_stream_in_feed(req
->feed
, codec
, codec
->bit_rate
/ 4);
1110 /* This doesn't work well when it turns off the only stream! */
1111 c
->switch_feed_streams
[i
] = -2;
1112 c
->feed_streams
[i
] = -2;
1117 if (c
->switch_feed_streams
[i
] >= 0 && c
->switch_feed_streams
[i
] != c
->feed_streams
[i
])
1118 action_required
= 1;
1121 return action_required
;
1125 static void do_switch_stream(HTTPContext
*c
, int i
)
1127 if (c
->switch_feed_streams
[i
] >= 0) {
1129 c
->feed_streams
[i
] = c
->switch_feed_streams
[i
];
1132 /* Now update the stream */
1134 c
->switch_feed_streams
[i
] = -1;
1137 /* XXX: factorize in utils.c ? */
1138 /* XXX: take care with different space meaning */
1139 static void skip_spaces(const char **pp
)
1143 while (*p
== ' ' || *p
== '\t')
1148 static void get_word(char *buf
, int buf_size
, const char **pp
)
1156 while (!isspace(*p
) && *p
!= '\0') {
1157 if ((q
- buf
) < buf_size
- 1)
1166 static int validate_acl(FFStream
*stream
, HTTPContext
*c
)
1168 enum IPAddressAction last_action
= IP_DENY
;
1170 struct in_addr
*src
= &c
->from_addr
.sin_addr
;
1171 unsigned long src_addr
= src
->s_addr
;
1173 for (acl
= stream
->acl
; acl
; acl
= acl
->next
) {
1174 if (src_addr
>= acl
->first
.s_addr
&& src_addr
<= acl
->last
.s_addr
)
1175 return (acl
->action
== IP_ALLOW
) ? 1 : 0;
1176 last_action
= acl
->action
;
1179 /* Nothing matched, so return not the last action */
1180 return (last_action
== IP_DENY
) ? 1 : 0;
1183 /* compute the real filename of a file by matching it without its
1184 extensions to all the stream filenames */
1185 static void compute_real_filename(char *filename
, int max_size
)
1192 /* compute filename by matching without the file extensions */
1193 av_strlcpy(file1
, filename
, sizeof(file1
));
1194 p
= strrchr(file1
, '.');
1197 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
1198 av_strlcpy(file2
, stream
->filename
, sizeof(file2
));
1199 p
= strrchr(file2
, '.');
1202 if (!strcmp(file1
, file2
)) {
1203 av_strlcpy(filename
, stream
->filename
, max_size
);
1218 /* parse http request and prepare header */
1219 static int http_parse_request(HTTPContext
*c
)
1222 enum RedirType redir_type
;
1224 char info
[1024], filename
[1024];
1228 const char *mime_type
;
1232 char *useragent
= 0;
1235 get_word(cmd
, sizeof(cmd
), (const char **)&p
);
1236 av_strlcpy(c
->method
, cmd
, sizeof(c
->method
));
1238 if (!strcmp(cmd
, "GET"))
1240 else if (!strcmp(cmd
, "POST"))
1245 get_word(url
, sizeof(url
), (const char **)&p
);
1246 av_strlcpy(c
->url
, url
, sizeof(c
->url
));
1248 get_word(protocol
, sizeof(protocol
), (const char **)&p
);
1249 if (strcmp(protocol
, "HTTP/1.0") && strcmp(protocol
, "HTTP/1.1"))
1252 av_strlcpy(c
->protocol
, protocol
, sizeof(c
->protocol
));
1255 http_log("New connection: %s %s\n", cmd
, url
);
1257 /* find the filename and the optional info string in the request */
1258 p
= strchr(url
, '?');
1260 av_strlcpy(info
, p
, sizeof(info
));
1265 av_strlcpy(filename
, url
+ ((*url
== '/') ? 1 : 0), sizeof(filename
)-1);
1267 for (p
= c
->buffer
; *p
&& *p
!= '\r' && *p
!= '\n'; ) {
1268 if (strncasecmp(p
, "User-Agent:", 11) == 0) {
1270 if (*useragent
&& *useragent
!= '\n' && isspace(*useragent
))
1274 p
= strchr(p
, '\n');
1281 redir_type
= REDIR_NONE
;
1282 if (match_ext(filename
, "asx")) {
1283 redir_type
= REDIR_ASX
;
1284 filename
[strlen(filename
)-1] = 'f';
1285 } else if (match_ext(filename
, "asf") &&
1286 (!useragent
|| strncasecmp(useragent
, "NSPlayer", 8) != 0)) {
1287 /* if this isn't WMP or lookalike, return the redirector file */
1288 redir_type
= REDIR_ASF
;
1289 } else if (match_ext(filename
, "rpm,ram")) {
1290 redir_type
= REDIR_RAM
;
1291 strcpy(filename
+ strlen(filename
)-2, "m");
1292 } else if (match_ext(filename
, "rtsp")) {
1293 redir_type
= REDIR_RTSP
;
1294 compute_real_filename(filename
, sizeof(filename
) - 1);
1295 } else if (match_ext(filename
, "sdp")) {
1296 redir_type
= REDIR_SDP
;
1297 compute_real_filename(filename
, sizeof(filename
) - 1);
1300 // "redirect" / request to index.html
1301 if (!strlen(filename
))
1302 av_strlcpy(filename
, "index.html", sizeof(filename
) - 1);
1304 stream
= first_stream
;
1305 while (stream
!= NULL
) {
1306 if (!strcmp(stream
->filename
, filename
) && validate_acl(stream
, c
))
1308 stream
= stream
->next
;
1310 if (stream
== NULL
) {
1311 snprintf(msg
, sizeof(msg
), "File '%s' not found", url
);
1316 memcpy(c
->feed_streams
, stream
->feed_streams
, sizeof(c
->feed_streams
));
1317 memset(c
->switch_feed_streams
, -1, sizeof(c
->switch_feed_streams
));
1319 if (stream
->stream_type
== STREAM_TYPE_REDIRECT
) {
1320 c
->http_error
= 301;
1322 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "HTTP/1.0 301 Moved\r\n");
1323 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "Location: %s\r\n", stream
->feed_filename
);
1324 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "Content-type: text/html\r\n");
1325 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "\r\n");
1326 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "<html><head><title>Moved</title></head><body>\r\n");
1327 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "You should be <a href=\"%s\">redirected</a>.\r\n", stream
->feed_filename
);
1328 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "</body></html>\r\n");
1330 /* prepare output buffer */
1331 c
->buffer_ptr
= c
->buffer
;
1333 c
->state
= HTTPSTATE_SEND_HEADER
;
1337 /* If this is WMP, get the rate information */
1338 if (extract_rates(ratebuf
, sizeof(ratebuf
), c
->buffer
)) {
1339 if (modify_current_stream(c
, ratebuf
)) {
1340 for (i
= 0; i
< sizeof(c
->feed_streams
) / sizeof(c
->feed_streams
[0]); i
++) {
1341 if (c
->switch_feed_streams
[i
] >= 0)
1342 do_switch_stream(c
, i
);
1347 /* If already streaming this feed, do not let start another feeder. */
1348 if (stream
->feed_opened
) {
1349 snprintf(msg
, sizeof(msg
), "This feed is already being received.");
1350 http_log("feed %s already being received\n", stream
->feed_filename
);
1354 if (c
->post
== 0 && stream
->stream_type
== STREAM_TYPE_LIVE
)
1355 current_bandwidth
+= stream
->bandwidth
;
1357 if (c
->post
== 0 && max_bandwidth
< current_bandwidth
) {
1358 c
->http_error
= 200;
1360 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "HTTP/1.0 200 Server too busy\r\n");
1361 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "Content-type: text/html\r\n");
1362 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "\r\n");
1363 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "<html><head><title>Too busy</title></head><body>\r\n");
1364 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");
1365 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "<p>The bandwidth being served (including your stream) is %lldkbit/sec, and this exceeds the limit of %lldkbit/sec.</p>\r\n",
1366 current_bandwidth
, max_bandwidth
);
1367 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "</body></html>\r\n");
1369 /* prepare output buffer */
1370 c
->buffer_ptr
= c
->buffer
;
1372 c
->state
= HTTPSTATE_SEND_HEADER
;
1376 if (redir_type
!= REDIR_NONE
) {
1379 for (p
= c
->buffer
; *p
&& *p
!= '\r' && *p
!= '\n'; ) {
1380 if (strncasecmp(p
, "Host:", 5) == 0) {
1384 p
= strchr(p
, '\n');
1395 while (isspace(*hostinfo
))
1398 eoh
= strchr(hostinfo
, '\n');
1400 if (eoh
[-1] == '\r')
1403 if (eoh
- hostinfo
< sizeof(hostbuf
) - 1) {
1404 memcpy(hostbuf
, hostinfo
, eoh
- hostinfo
);
1405 hostbuf
[eoh
- hostinfo
] = 0;
1407 c
->http_error
= 200;
1409 switch(redir_type
) {
1411 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "HTTP/1.0 200 ASX Follows\r\n");
1412 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "Content-type: video/x-ms-asf\r\n");
1413 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "\r\n");
1414 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "<ASX Version=\"3\">\r\n");
1415 //q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<!-- Autogenerated by ffserver -->\r\n");
1416 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n",
1417 hostbuf
, filename
, info
);
1418 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "</ASX>\r\n");
1421 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "HTTP/1.0 200 RAM Follows\r\n");
1422 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "Content-type: audio/x-pn-realaudio\r\n");
1423 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "\r\n");
1424 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "# Autogenerated by ffserver\r\n");
1425 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "http://%s/%s%s\r\n",
1426 hostbuf
, filename
, info
);
1429 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "HTTP/1.0 200 ASF Redirect follows\r\n");
1430 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "Content-type: video/x-ms-asf\r\n");
1431 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "\r\n");
1432 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "[Reference]\r\n");
1433 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "Ref1=http://%s/%s%s\r\n",
1434 hostbuf
, filename
, info
);
1438 char hostname
[256], *p
;
1439 /* extract only hostname */
1440 av_strlcpy(hostname
, hostbuf
, sizeof(hostname
));
1441 p
= strrchr(hostname
, ':');
1444 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "HTTP/1.0 200 RTSP Redirect follows\r\n");
1445 /* XXX: incorrect mime type ? */
1446 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "Content-type: application/x-rtsp\r\n");
1447 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "\r\n");
1448 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "rtsp://%s:%d/%s\r\n",
1449 hostname
, ntohs(my_rtsp_addr
.sin_port
),
1456 int sdp_data_size
, len
;
1457 struct sockaddr_in my_addr
;
1459 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "HTTP/1.0 200 OK\r\n");
1460 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "Content-type: application/sdp\r\n");
1461 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "\r\n");
1463 len
= sizeof(my_addr
);
1464 getsockname(c
->fd
, (struct sockaddr
*)&my_addr
, &len
);
1466 /* XXX: should use a dynamic buffer */
1467 sdp_data_size
= prepare_sdp_description(stream
,
1470 if (sdp_data_size
> 0) {
1471 memcpy(q
, sdp_data
, sdp_data_size
);
1483 /* prepare output buffer */
1484 c
->buffer_ptr
= c
->buffer
;
1486 c
->state
= HTTPSTATE_SEND_HEADER
;
1492 snprintf(msg
, sizeof(msg
), "ASX/RAM file not handled");
1496 stream
->conns_served
++;
1498 /* XXX: add there authenticate and IP match */
1501 /* if post, it means a feed is being sent */
1502 if (!stream
->is_feed
) {
1503 /* However it might be a status report from WMP! Let us log the
1504 * data as it might come in handy one day. */
1508 for (p
= c
->buffer
; *p
&& *p
!= '\r' && *p
!= '\n'; ) {
1509 if (strncasecmp(p
, "Pragma: log-line=", 17) == 0) {
1513 if (strncasecmp(p
, "Pragma: client-id=", 18) == 0)
1514 client_id
= strtol(p
+ 18, 0, 10);
1515 p
= strchr(p
, '\n');
1523 char *eol
= strchr(logline
, '\n');
1528 if (eol
[-1] == '\r')
1530 http_log("%.*s\n", (int) (eol
- logline
), logline
);
1531 c
->suppress_log
= 1;
1536 http_log("\nGot request:\n%s\n", c
->buffer
);
1539 if (client_id
&& extract_rates(ratebuf
, sizeof(ratebuf
), c
->buffer
)) {
1542 /* Now we have to find the client_id */
1543 for (wmpc
= first_http_ctx
; wmpc
; wmpc
= wmpc
->next
) {
1544 if (wmpc
->wmp_client_id
== client_id
)
1548 if (wmpc
&& modify_current_stream(wmpc
, ratebuf
))
1549 wmpc
->switch_pending
= 1;
1552 snprintf(msg
, sizeof(msg
), "POST command not handled");
1556 if (http_start_receive_data(c
) < 0) {
1557 snprintf(msg
, sizeof(msg
), "could not open feed");
1561 c
->state
= HTTPSTATE_RECEIVE_DATA
;
1566 if (strcmp(stream
->filename
+ strlen(stream
->filename
) - 4, ".asf") == 0)
1567 http_log("\nGot request:\n%s\n", c
->buffer
);
1570 if (c
->stream
->stream_type
== STREAM_TYPE_STATUS
)
1573 /* open input stream */
1574 if (open_input_stream(c
, info
) < 0) {
1575 snprintf(msg
, sizeof(msg
), "Input stream corresponding to '%s' not found", url
);
1579 /* prepare http header */
1581 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "HTTP/1.0 200 OK\r\n");
1582 mime_type
= c
->stream
->fmt
->mime_type
;
1584 mime_type
= "application/x-octet-stream";
1585 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "Pragma: no-cache\r\n");
1587 /* for asf, we need extra headers */
1588 if (!strcmp(c
->stream
->fmt
->name
,"asf_stream")) {
1589 /* Need to allocate a client id */
1591 c
->wmp_client_id
= av_random(&random_state
) & 0x7fffffff;
1593 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
);
1595 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "Content-Type: %s\r\n", mime_type
);
1596 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "\r\n");
1598 /* prepare output buffer */
1600 c
->buffer_ptr
= c
->buffer
;
1602 c
->state
= HTTPSTATE_SEND_HEADER
;
1605 c
->http_error
= 404;
1607 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "HTTP/1.0 404 Not Found\r\n");
1608 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "Content-type: %s\r\n", "text/html");
1609 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "\r\n");
1610 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "<HTML>\n");
1611 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n");
1612 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "<BODY>%s</BODY>\n", msg
);
1613 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "</HTML>\n");
1615 /* prepare output buffer */
1616 c
->buffer_ptr
= c
->buffer
;
1618 c
->state
= HTTPSTATE_SEND_HEADER
;
1622 c
->http_error
= 200; /* horrible : we use this value to avoid
1623 going to the send data state */
1624 c
->state
= HTTPSTATE_SEND_HEADER
;
1628 static void fmt_bytecount(ByteIOContext
*pb
, int64_t count
)
1630 static const char *suffix
= " kMGTP";
1633 for (s
= suffix
; count
>= 100000 && s
[1]; count
/= 1000, s
++);
1635 url_fprintf(pb
, "%"PRId64
"%c", count
, *s
);
1638 static void compute_status(HTTPContext
*c
)
1647 if (url_open_dyn_buf(&pb
) < 0) {
1648 /* XXX: return an error ? */
1649 c
->buffer_ptr
= c
->buffer
;
1650 c
->buffer_end
= c
->buffer
;
1654 url_fprintf(pb
, "HTTP/1.0 200 OK\r\n");
1655 url_fprintf(pb
, "Content-type: %s\r\n", "text/html");
1656 url_fprintf(pb
, "Pragma: no-cache\r\n");
1657 url_fprintf(pb
, "\r\n");
1659 url_fprintf(pb
, "<HEAD><TITLE>%s Status</TITLE>\n", program_name
);
1660 if (c
->stream
->feed_filename
[0])
1661 url_fprintf(pb
, "<link rel=\"shortcut icon\" href=\"%s\">\n", c
->stream
->feed_filename
);
1662 url_fprintf(pb
, "</HEAD>\n<BODY>");
1663 url_fprintf(pb
, "<H1>%s Status</H1>\n", program_name
);
1665 url_fprintf(pb
, "<H2>Available Streams</H2>\n");
1666 url_fprintf(pb
, "<TABLE cellspacing=0 cellpadding=4>\n");
1667 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");
1668 stream
= first_stream
;
1669 while (stream
!= NULL
) {
1670 char sfilename
[1024];
1673 if (stream
->feed
!= stream
) {
1674 av_strlcpy(sfilename
, stream
->filename
, sizeof(sfilename
) - 10);
1675 eosf
= sfilename
+ strlen(sfilename
);
1676 if (eosf
- sfilename
>= 4) {
1677 if (strcmp(eosf
- 4, ".asf") == 0)
1678 strcpy(eosf
- 4, ".asx");
1679 else if (strcmp(eosf
- 3, ".rm") == 0)
1680 strcpy(eosf
- 3, ".ram");
1681 else if (stream
->fmt
&& !strcmp(stream
->fmt
->name
, "rtp")) {
1682 /* generate a sample RTSP director if
1683 unicast. Generate an SDP redirector if
1685 eosf
= strrchr(sfilename
, '.');
1687 eosf
= sfilename
+ strlen(sfilename
);
1688 if (stream
->is_multicast
)
1689 strcpy(eosf
, ".sdp");
1691 strcpy(eosf
, ".rtsp");
1695 url_fprintf(pb
, "<TR><TD><A HREF=\"/%s\">%s</A> ",
1696 sfilename
, stream
->filename
);
1697 url_fprintf(pb
, "<td align=right> %d <td align=right> ",
1698 stream
->conns_served
);
1699 fmt_bytecount(pb
, stream
->bytes_served
);
1700 switch(stream
->stream_type
) {
1701 case STREAM_TYPE_LIVE
: {
1702 int audio_bit_rate
= 0;
1703 int video_bit_rate
= 0;
1704 const char *audio_codec_name
= "";
1705 const char *video_codec_name
= "";
1706 const char *audio_codec_name_extra
= "";
1707 const char *video_codec_name_extra
= "";
1709 for(i
=0;i
<stream
->nb_streams
;i
++) {
1710 AVStream
*st
= stream
->streams
[i
];
1711 AVCodec
*codec
= avcodec_find_encoder(st
->codec
->codec_id
);
1712 switch(st
->codec
->codec_type
) {
1713 case CODEC_TYPE_AUDIO
:
1714 audio_bit_rate
+= st
->codec
->bit_rate
;
1716 if (*audio_codec_name
)
1717 audio_codec_name_extra
= "...";
1718 audio_codec_name
= codec
->name
;
1721 case CODEC_TYPE_VIDEO
:
1722 video_bit_rate
+= st
->codec
->bit_rate
;
1724 if (*video_codec_name
)
1725 video_codec_name_extra
= "...";
1726 video_codec_name
= codec
->name
;
1729 case CODEC_TYPE_DATA
:
1730 video_bit_rate
+= st
->codec
->bit_rate
;
1736 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",
1739 video_bit_rate
/ 1000, video_codec_name
, video_codec_name_extra
,
1740 audio_bit_rate
/ 1000, audio_codec_name
, audio_codec_name_extra
);
1742 url_fprintf(pb
, "<TD>%s", stream
->feed
->filename
);
1744 url_fprintf(pb
, "<TD>%s", stream
->feed_filename
);
1745 url_fprintf(pb
, "\n");
1749 url_fprintf(pb
, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
1753 stream
= stream
->next
;
1755 url_fprintf(pb
, "</TABLE>\n");
1757 stream
= first_stream
;
1758 while (stream
!= NULL
) {
1759 if (stream
->feed
== stream
) {
1760 url_fprintf(pb
, "<h2>Feed %s</h2>", stream
->filename
);
1762 url_fprintf(pb
, "Running as pid %d.\n", stream
->pid
);
1764 #if defined(linux) && !defined(CONFIG_NOCUTILS)
1769 /* This is somewhat linux specific I guess */
1770 snprintf(ps_cmd
, sizeof(ps_cmd
),
1771 "ps -o \"%%cpu,cputime\" --no-headers %d",
1774 pid_stat
= popen(ps_cmd
, "r");
1779 if (fscanf(pid_stat
, "%10s %64s", cpuperc
,
1781 url_fprintf(pb
, "Currently using %s%% of the cpu. Total time used %s.\n",
1789 url_fprintf(pb
, "<p>");
1791 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");
1793 for (i
= 0; i
< stream
->nb_streams
; i
++) {
1794 AVStream
*st
= stream
->streams
[i
];
1795 AVCodec
*codec
= avcodec_find_encoder(st
->codec
->codec_id
);
1796 const char *type
= "unknown";
1797 char parameters
[64];
1801 switch(st
->codec
->codec_type
) {
1802 case CODEC_TYPE_AUDIO
:
1804 snprintf(parameters
, sizeof(parameters
), "%d channel(s), %d Hz", st
->codec
->channels
, st
->codec
->sample_rate
);
1806 case CODEC_TYPE_VIDEO
:
1808 snprintf(parameters
, sizeof(parameters
), "%dx%d, q=%d-%d, fps=%d", st
->codec
->width
, st
->codec
->height
,
1809 st
->codec
->qmin
, st
->codec
->qmax
, st
->codec
->time_base
.den
/ st
->codec
->time_base
.num
);
1814 url_fprintf(pb
, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
1815 i
, type
, st
->codec
->bit_rate
/1000, codec
? codec
->name
: "", parameters
);
1817 url_fprintf(pb
, "</table>\n");
1820 stream
= stream
->next
;
1826 AVCodecContext
*enc
;
1830 stream
= first_feed
;
1831 while (stream
!= NULL
) {
1832 url_fprintf(pb
, "<H1>Feed '%s'</H1>\n", stream
->filename
);
1833 url_fprintf(pb
, "<TABLE>\n");
1834 url_fprintf(pb
, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n");
1835 for(i
=0;i
<stream
->nb_streams
;i
++) {
1836 AVStream
*st
= stream
->streams
[i
];
1837 FeedData
*fdata
= st
->priv_data
;
1840 avcodec_string(buf
, sizeof(buf
), enc
);
1841 avg
= fdata
->avg_frame_size
* (float)enc
->rate
* 8.0;
1842 if (enc
->codec
->type
== CODEC_TYPE_AUDIO
&& enc
->frame_size
> 0)
1843 avg
/= enc
->frame_size
;
1844 url_fprintf(pb
, "<TR><TD>%s <TD> %d <TD> %"PRId64
" <TD> %0.1f\n",
1845 buf
, enc
->frame_number
, fdata
->data_count
, avg
/ 1000.0);
1847 url_fprintf(pb
, "</TABLE>\n");
1848 stream
= stream
->next_feed
;
1853 /* connection status */
1854 url_fprintf(pb
, "<H2>Connection Status</H2>\n");
1856 url_fprintf(pb
, "Number of connections: %d / %d<BR>\n",
1857 nb_connections
, nb_max_connections
);
1859 url_fprintf(pb
, "Bandwidth in use: %lldk / %lldk<BR>\n",
1860 current_bandwidth
, max_bandwidth
);
1862 url_fprintf(pb
, "<TABLE>\n");
1863 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");
1864 c1
= first_http_ctx
;
1866 while (c1
!= NULL
) {
1872 for (j
= 0; j
< c1
->stream
->nb_streams
; j
++) {
1873 if (!c1
->stream
->feed
)
1874 bitrate
+= c1
->stream
->streams
[j
]->codec
->bit_rate
;
1875 else if (c1
->feed_streams
[j
] >= 0)
1876 bitrate
+= c1
->stream
->feed
->streams
[c1
->feed_streams
[j
]]->codec
->bit_rate
;
1881 p
= inet_ntoa(c1
->from_addr
.sin_addr
);
1882 url_fprintf(pb
, "<TR><TD><B>%d</B><TD>%s%s<TD>%s<TD>%s<TD>%s<td align=right>",
1884 c1
->stream
? c1
->stream
->filename
: "",
1885 c1
->state
== HTTPSTATE_RECEIVE_DATA
? "(input)" : "",
1888 http_state
[c1
->state
]);
1889 fmt_bytecount(pb
, bitrate
);
1890 url_fprintf(pb
, "<td align=right>");
1891 fmt_bytecount(pb
, compute_datarate(&c1
->datarate
, c1
->data_count
) * 8);
1892 url_fprintf(pb
, "<td align=right>");
1893 fmt_bytecount(pb
, c1
->data_count
);
1894 url_fprintf(pb
, "\n");
1897 url_fprintf(pb
, "</TABLE>\n");
1902 url_fprintf(pb
, "<HR size=1 noshade>Generated at %s", p
);
1903 url_fprintf(pb
, "</BODY>\n</HTML>\n");
1905 len
= url_close_dyn_buf(pb
, &c
->pb_buffer
);
1906 c
->buffer_ptr
= c
->pb_buffer
;
1907 c
->buffer_end
= c
->pb_buffer
+ len
;
1910 /* check if the parser needs to be opened for stream i */
1911 static void open_parser(AVFormatContext
*s
, int i
)
1913 AVStream
*st
= s
->streams
[i
];
1916 if (!st
->codec
->codec
) {
1917 codec
= avcodec_find_decoder(st
->codec
->codec_id
);
1918 if (codec
&& (codec
->capabilities
& CODEC_CAP_PARSE_ONLY
)) {
1919 st
->codec
->parse_only
= 1;
1920 if (avcodec_open(st
->codec
, codec
) < 0)
1921 st
->codec
->parse_only
= 0;
1926 static int open_input_stream(HTTPContext
*c
, const char *info
)
1929 char input_filename
[1024];
1931 int buf_size
, i
, ret
;
1934 /* find file name */
1935 if (c
->stream
->feed
) {
1936 strcpy(input_filename
, c
->stream
->feed
->feed_filename
);
1937 buf_size
= FFM_PACKET_SIZE
;
1938 /* compute position (absolute time) */
1939 if (find_info_tag(buf
, sizeof(buf
), "date", info
)) {
1940 stream_pos
= parse_date(buf
, 0);
1941 if (stream_pos
== INT64_MIN
)
1943 } else if (find_info_tag(buf
, sizeof(buf
), "buffer", info
)) {
1944 int prebuffer
= strtol(buf
, 0, 10);
1945 stream_pos
= av_gettime() - prebuffer
* (int64_t)1000000;
1947 stream_pos
= av_gettime() - c
->stream
->prebuffer
* (int64_t)1000;
1949 strcpy(input_filename
, c
->stream
->feed_filename
);
1951 /* compute position (relative time) */
1952 if (find_info_tag(buf
, sizeof(buf
), "date", info
)) {
1953 stream_pos
= parse_date(buf
, 1);
1954 if (stream_pos
== INT64_MIN
)
1959 if (input_filename
[0] == '\0')
1963 { time_t when
= stream_pos
/ 1000000;
1964 http_log("Stream pos = %"PRId64
", time=%s", stream_pos
, ctime(&when
));
1969 if ((ret
= av_open_input_file(&s
, input_filename
, c
->stream
->ifmt
,
1970 buf_size
, c
->stream
->ap_in
)) < 0) {
1971 http_log("could not open %s: %d\n", input_filename
, ret
);
1974 s
->flags
|= AVFMT_FLAG_GENPTS
;
1976 av_find_stream_info(c
->fmt_in
);
1978 /* open each parser */
1979 for(i
=0;i
<s
->nb_streams
;i
++)
1982 /* choose stream as clock source (we favorize video stream if
1983 present) for packet sending */
1984 c
->pts_stream_index
= 0;
1985 for(i
=0;i
<c
->stream
->nb_streams
;i
++) {
1986 if (c
->pts_stream_index
== 0 &&
1987 c
->stream
->streams
[i
]->codec
->codec_type
== CODEC_TYPE_VIDEO
) {
1988 c
->pts_stream_index
= i
;
1993 if (c
->fmt_in
->iformat
->read_seek
)
1994 av_seek_frame(c
->fmt_in
, -1, stream_pos
, 0);
1996 /* set the start time (needed for maxtime and RTP packet timing) */
1997 c
->start_time
= cur_time
;
1998 c
->first_pts
= AV_NOPTS_VALUE
;
2002 /* return the server clock (in us) */
2003 static int64_t get_server_clock(HTTPContext
*c
)
2005 /* compute current pts value from system time */
2006 return (cur_time
- c
->start_time
) * 1000;
2009 /* return the estimated time at which the current packet must be sent
2011 static int64_t get_packet_send_clock(HTTPContext
*c
)
2013 int bytes_left
, bytes_sent
, frame_bytes
;
2015 frame_bytes
= c
->cur_frame_bytes
;
2016 if (frame_bytes
<= 0)
2019 bytes_left
= c
->buffer_end
- c
->buffer_ptr
;
2020 bytes_sent
= frame_bytes
- bytes_left
;
2021 return c
->cur_pts
+ (c
->cur_frame_duration
* bytes_sent
) / frame_bytes
;
2026 static int http_prepare_data(HTTPContext
*c
)
2029 AVFormatContext
*ctx
;
2031 av_freep(&c
->pb_buffer
);
2033 case HTTPSTATE_SEND_DATA_HEADER
:
2034 memset(&c
->fmt_ctx
, 0, sizeof(c
->fmt_ctx
));
2035 av_strlcpy(c
->fmt_ctx
.author
, c
->stream
->author
,
2036 sizeof(c
->fmt_ctx
.author
));
2037 av_strlcpy(c
->fmt_ctx
.comment
, c
->stream
->comment
,
2038 sizeof(c
->fmt_ctx
.comment
));
2039 av_strlcpy(c
->fmt_ctx
.copyright
, c
->stream
->copyright
,
2040 sizeof(c
->fmt_ctx
.copyright
));
2041 av_strlcpy(c
->fmt_ctx
.title
, c
->stream
->title
,
2042 sizeof(c
->fmt_ctx
.title
));
2044 for(i
=0;i
<c
->stream
->nb_streams
;i
++) {
2047 st
= av_mallocz(sizeof(AVStream
));
2048 c
->fmt_ctx
.streams
[i
] = st
;
2049 /* if file or feed, then just take streams from FFStream struct */
2050 if (!c
->stream
->feed
||
2051 c
->stream
->feed
== c
->stream
)
2052 src
= c
->stream
->streams
[i
];
2054 src
= c
->stream
->feed
->streams
[c
->stream
->feed_streams
[i
]];
2058 st
->codec
->frame_number
= 0; /* XXX: should be done in
2059 AVStream, not in codec */
2061 /* set output format parameters */
2062 c
->fmt_ctx
.oformat
= c
->stream
->fmt
;
2063 c
->fmt_ctx
.nb_streams
= c
->stream
->nb_streams
;
2065 c
->got_key_frame
= 0;
2067 /* prepare header and save header data in a stream */
2068 if (url_open_dyn_buf(&c
->fmt_ctx
.pb
) < 0) {
2069 /* XXX: potential leak */
2072 c
->fmt_ctx
.pb
->is_streamed
= 1;
2075 * HACK to avoid mpeg ps muxer to spit many underflow errors
2076 * Default value from FFmpeg
2077 * Try to set it use configuration option
2079 c
->fmt_ctx
.preload
= (int)(0.5*AV_TIME_BASE
);
2080 c
->fmt_ctx
.max_delay
= (int)(0.7*AV_TIME_BASE
);
2082 av_set_parameters(&c
->fmt_ctx
, NULL
);
2083 if (av_write_header(&c
->fmt_ctx
) < 0) {
2084 http_log("Error writing output header\n");
2088 len
= url_close_dyn_buf(c
->fmt_ctx
.pb
, &c
->pb_buffer
);
2089 c
->buffer_ptr
= c
->pb_buffer
;
2090 c
->buffer_end
= c
->pb_buffer
+ len
;
2092 c
->state
= HTTPSTATE_SEND_DATA
;
2093 c
->last_packet_sent
= 0;
2095 case HTTPSTATE_SEND_DATA
:
2096 /* find a new packet */
2097 /* read a packet from the input stream */
2098 if (c
->stream
->feed
)
2099 ffm_set_write_index(c
->fmt_in
,
2100 c
->stream
->feed
->feed_write_index
,
2101 c
->stream
->feed
->feed_size
);
2103 if (c
->stream
->max_time
&&
2104 c
->stream
->max_time
+ c
->start_time
- cur_time
< 0)
2105 /* We have timed out */
2106 c
->state
= HTTPSTATE_SEND_DATA_TRAILER
;
2110 if (av_read_frame(c
->fmt_in
, &pkt
) < 0) {
2111 if (c
->stream
->feed
&& c
->stream
->feed
->feed_opened
) {
2112 /* if coming from feed, it means we reached the end of the
2113 ffm file, so must wait for more data */
2114 c
->state
= HTTPSTATE_WAIT_FEED
;
2115 return 1; /* state changed */
2117 if (c
->stream
->loop
) {
2118 av_close_input_file(c
->fmt_in
);
2120 if (open_input_stream(c
, "") < 0)
2125 /* must send trailer now because eof or error */
2126 c
->state
= HTTPSTATE_SEND_DATA_TRAILER
;
2130 int source_index
= pkt
.stream_index
;
2131 /* update first pts if needed */
2132 if (c
->first_pts
== AV_NOPTS_VALUE
) {
2133 c
->first_pts
= av_rescale_q(pkt
.dts
, c
->fmt_in
->streams
[pkt
.stream_index
]->time_base
, AV_TIME_BASE_Q
);
2134 c
->start_time
= cur_time
;
2136 /* send it to the appropriate stream */
2137 if (c
->stream
->feed
) {
2138 /* if coming from a feed, select the right stream */
2139 if (c
->switch_pending
) {
2140 c
->switch_pending
= 0;
2141 for(i
=0;i
<c
->stream
->nb_streams
;i
++) {
2142 if (c
->switch_feed_streams
[i
] == pkt
.stream_index
)
2143 if (pkt
.flags
& PKT_FLAG_KEY
)
2144 do_switch_stream(c
, i
);
2145 if (c
->switch_feed_streams
[i
] >= 0)
2146 c
->switch_pending
= 1;
2149 for(i
=0;i
<c
->stream
->nb_streams
;i
++) {
2150 if (c
->feed_streams
[i
] == pkt
.stream_index
) {
2151 AVStream
*st
= c
->fmt_in
->streams
[source_index
];
2152 pkt
.stream_index
= i
;
2153 if (pkt
.flags
& PKT_FLAG_KEY
&&
2154 (st
->codec
->codec_type
== CODEC_TYPE_VIDEO
||
2155 c
->stream
->nb_streams
== 1))
2156 c
->got_key_frame
= 1;
2157 if (!c
->stream
->send_on_key
|| c
->got_key_frame
)
2162 AVCodecContext
*codec
;
2163 AVStream
*ist
, *ost
;
2165 ist
= c
->fmt_in
->streams
[source_index
];
2166 /* specific handling for RTP: we use several
2167 output stream (one for each RTP
2168 connection). XXX: need more abstract handling */
2169 if (c
->is_packetized
) {
2170 /* compute send time and duration */
2171 c
->cur_pts
= av_rescale_q(pkt
.dts
, ist
->time_base
, AV_TIME_BASE_Q
);
2172 if (ist
->start_time
!= AV_NOPTS_VALUE
)
2173 c
->cur_pts
-= av_rescale_q(ist
->start_time
, ist
->time_base
, AV_TIME_BASE_Q
);
2174 c
->cur_frame_duration
= av_rescale_q(pkt
.duration
, ist
->time_base
, AV_TIME_BASE_Q
);
2176 printf("index=%d pts=%0.3f duration=%0.6f\n",
2178 (double)c
->cur_pts
/
2180 (double)c
->cur_frame_duration
/
2183 /* find RTP context */
2184 c
->packet_stream_index
= pkt
.stream_index
;
2185 ctx
= c
->rtp_ctx
[c
->packet_stream_index
];
2187 av_free_packet(&pkt
);
2190 codec
= ctx
->streams
[0]->codec
;
2191 /* only one stream per RTP connection */
2192 pkt
.stream_index
= 0;
2196 codec
= ctx
->streams
[pkt
.stream_index
]->codec
;
2199 if (c
->is_packetized
) {
2200 int max_packet_size
;
2201 if (c
->rtp_protocol
== RTSP_PROTOCOL_RTP_TCP
)
2202 max_packet_size
= RTSP_TCP_MAX_PACKET_SIZE
;
2204 max_packet_size
= url_get_max_packet_size(c
->rtp_handles
[c
->packet_stream_index
]);
2205 ret
= url_open_dyn_packet_buf(&ctx
->pb
, max_packet_size
);
2207 ret
= url_open_dyn_buf(&ctx
->pb
);
2210 /* XXX: potential leak */
2213 ost
= ctx
->streams
[pkt
.stream_index
];
2215 ctx
->pb
->is_streamed
= 1;
2216 if (pkt
.dts
!= AV_NOPTS_VALUE
)
2217 pkt
.dts
= av_rescale_q(pkt
.dts
, ist
->time_base
, ost
->time_base
);
2218 if (pkt
.pts
!= AV_NOPTS_VALUE
)
2219 pkt
.pts
= av_rescale_q(pkt
.pts
, ist
->time_base
, ost
->time_base
);
2220 pkt
.duration
= av_rescale_q(pkt
.duration
, ist
->time_base
, ost
->time_base
);
2221 if (av_write_frame(ctx
, &pkt
) < 0) {
2222 http_log("Error writing frame to output\n");
2223 c
->state
= HTTPSTATE_SEND_DATA_TRAILER
;
2226 len
= url_close_dyn_buf(ctx
->pb
, &c
->pb_buffer
);
2227 c
->cur_frame_bytes
= len
;
2228 c
->buffer_ptr
= c
->pb_buffer
;
2229 c
->buffer_end
= c
->pb_buffer
+ len
;
2231 codec
->frame_number
++;
2233 av_free_packet(&pkt
);
2237 av_free_packet(&pkt
);
2242 case HTTPSTATE_SEND_DATA_TRAILER
:
2243 /* last packet test ? */
2244 if (c
->last_packet_sent
|| c
->is_packetized
)
2247 /* prepare header */
2248 if (url_open_dyn_buf(&ctx
->pb
) < 0) {
2249 /* XXX: potential leak */
2252 c
->fmt_ctx
.pb
->is_streamed
= 1;
2253 av_write_trailer(ctx
);
2254 len
= url_close_dyn_buf(ctx
->pb
, &c
->pb_buffer
);
2255 c
->buffer_ptr
= c
->pb_buffer
;
2256 c
->buffer_end
= c
->pb_buffer
+ len
;
2258 c
->last_packet_sent
= 1;
2264 /* should convert the format at the same time */
2265 /* send data starting at c->buffer_ptr to the output connection
2266 (either UDP or TCP connection) */
2267 static int http_send_data(HTTPContext
*c
)
2272 if (c
->buffer_ptr
>= c
->buffer_end
) {
2273 ret
= http_prepare_data(c
);
2277 /* state change requested */
2280 if (c
->is_packetized
) {
2281 /* RTP data output */
2282 len
= c
->buffer_end
- c
->buffer_ptr
;
2284 /* fail safe - should never happen */
2286 c
->buffer_ptr
= c
->buffer_end
;
2289 len
= (c
->buffer_ptr
[0] << 24) |
2290 (c
->buffer_ptr
[1] << 16) |
2291 (c
->buffer_ptr
[2] << 8) |
2293 if (len
> (c
->buffer_end
- c
->buffer_ptr
))
2295 if ((get_packet_send_clock(c
) - get_server_clock(c
)) > 0) {
2296 /* nothing to send yet: we can wait */
2300 c
->data_count
+= len
;
2301 update_datarate(&c
->datarate
, c
->data_count
);
2303 c
->stream
->bytes_served
+= len
;
2305 if (c
->rtp_protocol
== RTSP_PROTOCOL_RTP_TCP
) {
2306 /* RTP packets are sent inside the RTSP TCP connection */
2308 int interleaved_index
, size
;
2310 HTTPContext
*rtsp_c
;
2313 /* if no RTSP connection left, error */
2316 /* if already sending something, then wait. */
2317 if (rtsp_c
->state
!= RTSPSTATE_WAIT_REQUEST
)
2319 if (url_open_dyn_buf(&pb
) < 0)
2321 interleaved_index
= c
->packet_stream_index
* 2;
2322 /* RTCP packets are sent at odd indexes */
2323 if (c
->buffer_ptr
[1] == 200)
2324 interleaved_index
++;
2325 /* write RTSP TCP header */
2327 header
[1] = interleaved_index
;
2328 header
[2] = len
>> 8;
2330 put_buffer(pb
, header
, 4);
2331 /* write RTP packet data */
2333 put_buffer(pb
, c
->buffer_ptr
, len
);
2334 size
= url_close_dyn_buf(pb
, &c
->packet_buffer
);
2335 /* prepare asynchronous TCP sending */
2336 rtsp_c
->packet_buffer_ptr
= c
->packet_buffer
;
2337 rtsp_c
->packet_buffer_end
= c
->packet_buffer
+ size
;
2338 c
->buffer_ptr
+= len
;
2340 /* send everything we can NOW */
2341 len
= send(rtsp_c
->fd
, rtsp_c
->packet_buffer_ptr
,
2342 rtsp_c
->packet_buffer_end
- rtsp_c
->packet_buffer_ptr
, 0);
2344 rtsp_c
->packet_buffer_ptr
+= len
;
2345 if (rtsp_c
->packet_buffer_ptr
< rtsp_c
->packet_buffer_end
) {
2346 /* if we could not send all the data, we will
2347 send it later, so a new state is needed to
2348 "lock" the RTSP TCP connection */
2349 rtsp_c
->state
= RTSPSTATE_SEND_PACKET
;
2352 /* all data has been sent */
2353 av_freep(&c
->packet_buffer
);
2355 /* send RTP packet directly in UDP */
2357 url_write(c
->rtp_handles
[c
->packet_stream_index
],
2358 c
->buffer_ptr
, len
);
2359 c
->buffer_ptr
+= len
;
2360 /* here we continue as we can send several packets per 10 ms slot */
2363 /* TCP data output */
2364 len
= send(c
->fd
, c
->buffer_ptr
, c
->buffer_end
- c
->buffer_ptr
, 0);
2366 if (ff_neterrno() != FF_NETERROR(EAGAIN
) &&
2367 ff_neterrno() != FF_NETERROR(EINTR
))
2368 /* error : close connection */
2373 c
->buffer_ptr
+= len
;
2375 c
->data_count
+= len
;
2376 update_datarate(&c
->datarate
, c
->data_count
);
2378 c
->stream
->bytes_served
+= len
;
2386 static int http_start_receive_data(HTTPContext
*c
)
2390 if (c
->stream
->feed_opened
)
2393 /* Don't permit writing to this one */
2394 if (c
->stream
->readonly
)
2398 fd
= open(c
->stream
->feed_filename
, O_RDWR
);
2400 http_log("Error opening feeder file: %s\n", strerror(errno
));
2405 c
->stream
->feed_write_index
= ffm_read_write_index(fd
);
2406 c
->stream
->feed_size
= lseek(fd
, 0, SEEK_END
);
2407 lseek(fd
, 0, SEEK_SET
);
2409 /* init buffer input */
2410 c
->buffer_ptr
= c
->buffer
;
2411 c
->buffer_end
= c
->buffer
+ FFM_PACKET_SIZE
;
2412 c
->stream
->feed_opened
= 1;
2416 static int http_receive_data(HTTPContext
*c
)
2420 if (c
->buffer_end
> c
->buffer_ptr
) {
2423 len
= recv(c
->fd
, c
->buffer_ptr
, c
->buffer_end
- c
->buffer_ptr
, 0);
2425 if (ff_neterrno() != FF_NETERROR(EAGAIN
) &&
2426 ff_neterrno() != FF_NETERROR(EINTR
))
2427 /* error : close connection */
2429 } else if (len
== 0)
2430 /* end of connection : close it */
2433 c
->buffer_ptr
+= len
;
2434 c
->data_count
+= len
;
2435 update_datarate(&c
->datarate
, c
->data_count
);
2439 if (c
->buffer_ptr
- c
->buffer
>= 2 && c
->data_count
> FFM_PACKET_SIZE
) {
2440 if (c
->buffer
[0] != 'f' ||
2441 c
->buffer
[1] != 'm') {
2442 http_log("Feed stream has become desynchronized -- disconnecting\n");
2447 if (c
->buffer_ptr
>= c
->buffer_end
) {
2448 FFStream
*feed
= c
->stream
;
2449 /* a packet has been received : write it in the store, except
2451 if (c
->data_count
> FFM_PACKET_SIZE
) {
2453 // printf("writing pos=0x%"PRIx64" size=0x%"PRIx64"\n", feed->feed_write_index, feed->feed_size);
2454 /* XXX: use llseek or url_seek */
2455 lseek(c
->feed_fd
, feed
->feed_write_index
, SEEK_SET
);
2456 if (write(c
->feed_fd
, c
->buffer
, FFM_PACKET_SIZE
) < 0) {
2457 http_log("Error writing to feed file: %s\n", strerror(errno
));
2461 feed
->feed_write_index
+= FFM_PACKET_SIZE
;
2462 /* update file size */
2463 if (feed
->feed_write_index
> c
->stream
->feed_size
)
2464 feed
->feed_size
= feed
->feed_write_index
;
2466 /* handle wrap around if max file size reached */
2467 if (c
->stream
->feed_max_size
&& feed
->feed_write_index
>= c
->stream
->feed_max_size
)
2468 feed
->feed_write_index
= FFM_PACKET_SIZE
;
2471 ffm_write_write_index(c
->feed_fd
, feed
->feed_write_index
);
2473 /* wake up any waiting connections */
2474 for(c1
= first_http_ctx
; c1
!= NULL
; c1
= c1
->next
) {
2475 if (c1
->state
== HTTPSTATE_WAIT_FEED
&&
2476 c1
->stream
->feed
== c
->stream
->feed
)
2477 c1
->state
= HTTPSTATE_SEND_DATA
;
2480 /* We have a header in our hands that contains useful data */
2481 AVFormatContext
*s
= NULL
;
2483 AVInputFormat
*fmt_in
;
2486 /* use feed output format name to find corresponding input format */
2487 fmt_in
= av_find_input_format(feed
->fmt
->name
);
2491 url_open_buf(&pb
, c
->buffer
, c
->buffer_end
- c
->buffer
, URL_RDONLY
);
2492 pb
->is_streamed
= 1;
2494 if (av_open_input_stream(&s
, pb
, c
->stream
->feed_filename
, fmt_in
, NULL
) < 0) {
2499 /* Now we have the actual streams */
2500 if (s
->nb_streams
!= feed
->nb_streams
) {
2501 av_close_input_stream(s
);
2506 for (i
= 0; i
< s
->nb_streams
; i
++) {
2507 AVStream
*fst
= feed
->streams
[i
];
2508 AVStream
*st
= s
->streams
[i
];
2509 memcpy(fst
->codec
, st
->codec
, sizeof(AVCodecContext
));
2510 if (fst
->codec
->extradata_size
) {
2511 fst
->codec
->extradata
= av_malloc(fst
->codec
->extradata_size
);
2512 if (!fst
->codec
->extradata
)
2514 memcpy(fst
->codec
->extradata
, st
->codec
->extradata
,
2515 fst
->codec
->extradata_size
);
2519 av_close_input_stream(s
);
2522 c
->buffer_ptr
= c
->buffer
;
2527 c
->stream
->feed_opened
= 0;
2529 /* wake up any waiting connections to stop waiting for feed */
2530 for(c1
= first_http_ctx
; c1
!= NULL
; c1
= c1
->next
) {
2531 if (c1
->state
== HTTPSTATE_WAIT_FEED
&&
2532 c1
->stream
->feed
== c
->stream
->feed
)
2533 c1
->state
= HTTPSTATE_SEND_DATA_TRAILER
;
2538 /********************************************************************/
2541 static void rtsp_reply_header(HTTPContext
*c
, enum RTSPStatusCode error_number
)
2548 switch(error_number
) {
2549 case RTSP_STATUS_OK
:
2552 case RTSP_STATUS_METHOD
:
2553 str
= "Method Not Allowed";
2555 case RTSP_STATUS_BANDWIDTH
:
2556 str
= "Not Enough Bandwidth";
2558 case RTSP_STATUS_SESSION
:
2559 str
= "Session Not Found";
2561 case RTSP_STATUS_STATE
:
2562 str
= "Method Not Valid in This State";
2564 case RTSP_STATUS_AGGREGATE
:
2565 str
= "Aggregate operation not allowed";
2567 case RTSP_STATUS_ONLY_AGGREGATE
:
2568 str
= "Only aggregate operation allowed";
2570 case RTSP_STATUS_TRANSPORT
:
2571 str
= "Unsupported transport";
2573 case RTSP_STATUS_INTERNAL
:
2574 str
= "Internal Server Error";
2576 case RTSP_STATUS_SERVICE
:
2577 str
= "Service Unavailable";
2579 case RTSP_STATUS_VERSION
:
2580 str
= "RTSP Version not supported";
2583 str
= "Unknown Error";
2587 url_fprintf(c
->pb
, "RTSP/1.0 %d %s\r\n", error_number
, str
);
2588 url_fprintf(c
->pb
, "CSeq: %d\r\n", c
->seq
);
2590 /* output GMT time */
2594 p
= buf2
+ strlen(p
) - 1;
2597 url_fprintf(c
->pb
, "Date: %s GMT\r\n", buf2
);
2600 static void rtsp_reply_error(HTTPContext
*c
, enum RTSPStatusCode error_number
)
2602 rtsp_reply_header(c
, error_number
);
2603 url_fprintf(c
->pb
, "\r\n");
2606 static int rtsp_parse_request(HTTPContext
*c
)
2608 const char *p
, *p1
, *p2
;
2614 RTSPHeader header1
, *header
= &header1
;
2616 c
->buffer_ptr
[0] = '\0';
2619 get_word(cmd
, sizeof(cmd
), &p
);
2620 get_word(url
, sizeof(url
), &p
);
2621 get_word(protocol
, sizeof(protocol
), &p
);
2623 av_strlcpy(c
->method
, cmd
, sizeof(c
->method
));
2624 av_strlcpy(c
->url
, url
, sizeof(c
->url
));
2625 av_strlcpy(c
->protocol
, protocol
, sizeof(c
->protocol
));
2627 if (url_open_dyn_buf(&c
->pb
) < 0) {
2628 /* XXX: cannot do more */
2629 c
->pb
= NULL
; /* safety */
2633 /* check version name */
2634 if (strcmp(protocol
, "RTSP/1.0") != 0) {
2635 rtsp_reply_error(c
, RTSP_STATUS_VERSION
);
2639 /* parse each header line */
2640 memset(header
, 0, sizeof(RTSPHeader
));
2641 /* skip to next line */
2642 while (*p
!= '\n' && *p
!= '\0')
2646 while (*p
!= '\0') {
2647 p1
= strchr(p
, '\n');
2651 if (p2
> p
&& p2
[-1] == '\r')
2653 /* skip empty line */
2657 if (len
> sizeof(line
) - 1)
2658 len
= sizeof(line
) - 1;
2659 memcpy(line
, p
, len
);
2661 rtsp_parse_line(header
, line
);
2665 /* handle sequence number */
2666 c
->seq
= header
->seq
;
2668 if (!strcmp(cmd
, "DESCRIBE"))
2669 rtsp_cmd_describe(c
, url
);
2670 else if (!strcmp(cmd
, "OPTIONS"))
2671 rtsp_cmd_options(c
, url
);
2672 else if (!strcmp(cmd
, "SETUP"))
2673 rtsp_cmd_setup(c
, url
, header
);
2674 else if (!strcmp(cmd
, "PLAY"))
2675 rtsp_cmd_play(c
, url
, header
);
2676 else if (!strcmp(cmd
, "PAUSE"))
2677 rtsp_cmd_pause(c
, url
, header
);
2678 else if (!strcmp(cmd
, "TEARDOWN"))
2679 rtsp_cmd_teardown(c
, url
, header
);
2681 rtsp_reply_error(c
, RTSP_STATUS_METHOD
);
2684 len
= url_close_dyn_buf(c
->pb
, &c
->pb_buffer
);
2685 c
->pb
= NULL
; /* safety */
2687 /* XXX: cannot do more */
2690 c
->buffer_ptr
= c
->pb_buffer
;
2691 c
->buffer_end
= c
->pb_buffer
+ len
;
2692 c
->state
= RTSPSTATE_SEND_REPLY
;
2696 static int prepare_sdp_description(FFStream
*stream
, uint8_t **pbuffer
,
2697 struct in_addr my_ip
)
2699 AVFormatContext
*avc
;
2700 AVStream avs
[MAX_STREAMS
];
2703 avc
= av_alloc_format_context();
2707 if (stream
->title
[0] != 0) {
2708 av_strlcpy(avc
->title
, stream
->title
, sizeof(avc
->title
));
2710 av_strlcpy(avc
->title
, "No Title", sizeof(avc
->title
));
2712 avc
->nb_streams
= stream
->nb_streams
;
2713 if (stream
->is_multicast
) {
2714 snprintf(avc
->filename
, 1024, "rtp://%s:%d?multicast=1?ttl=%d",
2715 inet_ntoa(stream
->multicast_ip
),
2716 stream
->multicast_port
, stream
->multicast_ttl
);
2719 for(i
= 0; i
< stream
->nb_streams
; i
++) {
2720 avc
->streams
[i
] = &avs
[i
];
2721 avc
->streams
[i
]->codec
= stream
->streams
[i
]->codec
;
2723 *pbuffer
= av_mallocz(2048);
2724 avf_sdp_create(&avc
, 1, *pbuffer
, 2048);
2727 return strlen(*pbuffer
);
2730 static void rtsp_cmd_options(HTTPContext
*c
, const char *url
)
2732 // rtsp_reply_header(c, RTSP_STATUS_OK);
2733 url_fprintf(c
->pb
, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK
, "OK");
2734 url_fprintf(c
->pb
, "CSeq: %d\r\n", c
->seq
);
2735 url_fprintf(c
->pb
, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
2736 url_fprintf(c
->pb
, "\r\n");
2739 static void rtsp_cmd_describe(HTTPContext
*c
, const char *url
)
2745 int content_length
, len
;
2746 struct sockaddr_in my_addr
;
2748 /* find which url is asked */
2749 url_split(NULL
, 0, NULL
, 0, NULL
, 0, NULL
, path1
, sizeof(path1
), url
);
2754 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
2755 if (!stream
->is_feed
&&
2756 stream
->fmt
&& !strcmp(stream
->fmt
->name
, "rtp") &&
2757 !strcmp(path
, stream
->filename
)) {
2761 /* no stream found */
2762 rtsp_reply_error(c
, RTSP_STATUS_SERVICE
); /* XXX: right error ? */
2766 /* prepare the media description in sdp format */
2768 /* get the host IP */
2769 len
= sizeof(my_addr
);
2770 getsockname(c
->fd
, (struct sockaddr
*)&my_addr
, &len
);
2771 content_length
= prepare_sdp_description(stream
, &content
, my_addr
.sin_addr
);
2772 if (content_length
< 0) {
2773 rtsp_reply_error(c
, RTSP_STATUS_INTERNAL
);
2776 rtsp_reply_header(c
, RTSP_STATUS_OK
);
2777 url_fprintf(c
->pb
, "Content-Type: application/sdp\r\n");
2778 url_fprintf(c
->pb
, "Content-Length: %d\r\n", content_length
);
2779 url_fprintf(c
->pb
, "\r\n");
2780 put_buffer(c
->pb
, content
, content_length
);
2783 static HTTPContext
*find_rtp_session(const char *session_id
)
2787 if (session_id
[0] == '\0')
2790 for(c
= first_http_ctx
; c
!= NULL
; c
= c
->next
) {
2791 if (!strcmp(c
->session_id
, session_id
))
2797 static RTSPTransportField
*find_transport(RTSPHeader
*h
, enum RTSPProtocol protocol
)
2799 RTSPTransportField
*th
;
2802 for(i
=0;i
<h
->nb_transports
;i
++) {
2803 th
= &h
->transports
[i
];
2804 if (th
->protocol
== protocol
)
2810 static void rtsp_cmd_setup(HTTPContext
*c
, const char *url
,
2814 int stream_index
, port
;
2819 RTSPTransportField
*th
;
2820 struct sockaddr_in dest_addr
;
2821 RTSPActionServerSetup setup
;
2823 /* find which url is asked */
2824 url_split(NULL
, 0, NULL
, 0, NULL
, 0, NULL
, path1
, sizeof(path1
), url
);
2829 /* now check each stream */
2830 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
2831 if (!stream
->is_feed
&&
2832 stream
->fmt
&& !strcmp(stream
->fmt
->name
, "rtp")) {
2833 /* accept aggregate filenames only if single stream */
2834 if (!strcmp(path
, stream
->filename
)) {
2835 if (stream
->nb_streams
!= 1) {
2836 rtsp_reply_error(c
, RTSP_STATUS_AGGREGATE
);
2843 for(stream_index
= 0; stream_index
< stream
->nb_streams
;
2845 snprintf(buf
, sizeof(buf
), "%s/streamid=%d",
2846 stream
->filename
, stream_index
);
2847 if (!strcmp(path
, buf
))
2852 /* no stream found */
2853 rtsp_reply_error(c
, RTSP_STATUS_SERVICE
); /* XXX: right error ? */
2857 /* generate session id if needed */
2858 if (h
->session_id
[0] == '\0')
2859 snprintf(h
->session_id
, sizeof(h
->session_id
), "%08x%08x",
2860 av_random(&random_state
), av_random(&random_state
));
2862 /* find rtp session, and create it if none found */
2863 rtp_c
= find_rtp_session(h
->session_id
);
2865 /* always prefer UDP */
2866 th
= find_transport(h
, RTSP_PROTOCOL_RTP_UDP
);
2868 th
= find_transport(h
, RTSP_PROTOCOL_RTP_TCP
);
2870 rtsp_reply_error(c
, RTSP_STATUS_TRANSPORT
);
2875 rtp_c
= rtp_new_connection(&c
->from_addr
, stream
, h
->session_id
,
2878 rtsp_reply_error(c
, RTSP_STATUS_BANDWIDTH
);
2882 /* open input stream */
2883 if (open_input_stream(rtp_c
, "") < 0) {
2884 rtsp_reply_error(c
, RTSP_STATUS_INTERNAL
);
2889 /* test if stream is OK (test needed because several SETUP needs
2890 to be done for a given file) */
2891 if (rtp_c
->stream
!= stream
) {
2892 rtsp_reply_error(c
, RTSP_STATUS_SERVICE
);
2896 /* test if stream is already set up */
2897 if (rtp_c
->rtp_ctx
[stream_index
]) {
2898 rtsp_reply_error(c
, RTSP_STATUS_STATE
);
2902 /* check transport */
2903 th
= find_transport(h
, rtp_c
->rtp_protocol
);
2904 if (!th
|| (th
->protocol
== RTSP_PROTOCOL_RTP_UDP
&&
2905 th
->client_port_min
<= 0)) {
2906 rtsp_reply_error(c
, RTSP_STATUS_TRANSPORT
);
2910 /* setup default options */
2911 setup
.transport_option
[0] = '\0';
2912 dest_addr
= rtp_c
->from_addr
;
2913 dest_addr
.sin_port
= htons(th
->client_port_min
);
2916 if (rtp_new_av_stream(rtp_c
, stream_index
, &dest_addr
, c
) < 0) {
2917 rtsp_reply_error(c
, RTSP_STATUS_TRANSPORT
);
2921 /* now everything is OK, so we can send the connection parameters */
2922 rtsp_reply_header(c
, RTSP_STATUS_OK
);
2924 url_fprintf(c
->pb
, "Session: %s\r\n", rtp_c
->session_id
);
2926 switch(rtp_c
->rtp_protocol
) {
2927 case RTSP_PROTOCOL_RTP_UDP
:
2928 port
= rtp_get_local_port(rtp_c
->rtp_handles
[stream_index
]);
2929 url_fprintf(c
->pb
, "Transport: RTP/AVP/UDP;unicast;"
2930 "client_port=%d-%d;server_port=%d-%d",
2931 th
->client_port_min
, th
->client_port_min
+ 1,
2934 case RTSP_PROTOCOL_RTP_TCP
:
2935 url_fprintf(c
->pb
, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
2936 stream_index
* 2, stream_index
* 2 + 1);
2941 if (setup
.transport_option
[0] != '\0')
2942 url_fprintf(c
->pb
, ";%s", setup
.transport_option
);
2943 url_fprintf(c
->pb
, "\r\n");
2946 url_fprintf(c
->pb
, "\r\n");
2950 /* find an rtp connection by using the session ID. Check consistency
2952 static HTTPContext
*find_rtp_session_with_url(const char *url
,
2953 const char *session_id
)
2961 rtp_c
= find_rtp_session(session_id
);
2965 /* find which url is asked */
2966 url_split(NULL
, 0, NULL
, 0, NULL
, 0, NULL
, path1
, sizeof(path1
), url
);
2970 if(!strcmp(path
, rtp_c
->stream
->filename
)) return rtp_c
;
2971 for(s
=0; s
<rtp_c
->stream
->nb_streams
; ++s
) {
2972 snprintf(buf
, sizeof(buf
), "%s/streamid=%d",
2973 rtp_c
->stream
->filename
, s
);
2974 if(!strncmp(path
, buf
, sizeof(buf
))) {
2975 // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
2982 static void rtsp_cmd_play(HTTPContext
*c
, const char *url
, RTSPHeader
*h
)
2986 rtp_c
= find_rtp_session_with_url(url
, h
->session_id
);
2988 rtsp_reply_error(c
, RTSP_STATUS_SESSION
);
2992 if (rtp_c
->state
!= HTTPSTATE_SEND_DATA
&&
2993 rtp_c
->state
!= HTTPSTATE_WAIT_FEED
&&
2994 rtp_c
->state
!= HTTPSTATE_READY
) {
2995 rtsp_reply_error(c
, RTSP_STATUS_STATE
);
3000 /* XXX: seek in stream */
3001 if (h
->range_start
!= AV_NOPTS_VALUE
) {
3002 printf("range_start=%0.3f\n", (double)h
->range_start
/ AV_TIME_BASE
);
3003 av_seek_frame(rtp_c
->fmt_in
, -1, h
->range_start
);
3007 rtp_c
->state
= HTTPSTATE_SEND_DATA
;
3009 /* now everything is OK, so we can send the connection parameters */
3010 rtsp_reply_header(c
, RTSP_STATUS_OK
);
3012 url_fprintf(c
->pb
, "Session: %s\r\n", rtp_c
->session_id
);
3013 url_fprintf(c
->pb
, "\r\n");
3016 static void rtsp_cmd_pause(HTTPContext
*c
, const char *url
, RTSPHeader
*h
)
3020 rtp_c
= find_rtp_session_with_url(url
, h
->session_id
);
3022 rtsp_reply_error(c
, RTSP_STATUS_SESSION
);
3026 if (rtp_c
->state
!= HTTPSTATE_SEND_DATA
&&
3027 rtp_c
->state
!= HTTPSTATE_WAIT_FEED
) {
3028 rtsp_reply_error(c
, RTSP_STATUS_STATE
);
3032 rtp_c
->state
= HTTPSTATE_READY
;
3033 rtp_c
->first_pts
= AV_NOPTS_VALUE
;
3034 /* now everything is OK, so we can send the connection parameters */
3035 rtsp_reply_header(c
, RTSP_STATUS_OK
);
3037 url_fprintf(c
->pb
, "Session: %s\r\n", rtp_c
->session_id
);
3038 url_fprintf(c
->pb
, "\r\n");
3041 static void rtsp_cmd_teardown(HTTPContext
*c
, const char *url
, RTSPHeader
*h
)
3044 char session_id
[32];
3046 rtp_c
= find_rtp_session_with_url(url
, h
->session_id
);
3048 rtsp_reply_error(c
, RTSP_STATUS_SESSION
);
3052 av_strlcpy(session_id
, rtp_c
->session_id
, sizeof(session_id
));
3054 /* abort the session */
3055 close_connection(rtp_c
);
3057 /* now everything is OK, so we can send the connection parameters */
3058 rtsp_reply_header(c
, RTSP_STATUS_OK
);
3060 url_fprintf(c
->pb
, "Session: %s\r\n", session_id
);
3061 url_fprintf(c
->pb
, "\r\n");
3065 /********************************************************************/
3068 static HTTPContext
*rtp_new_connection(struct sockaddr_in
*from_addr
,
3069 FFStream
*stream
, const char *session_id
,
3070 enum RTSPProtocol rtp_protocol
)
3072 HTTPContext
*c
= NULL
;
3073 const char *proto_str
;
3075 /* XXX: should output a warning page when coming
3076 close to the connection limit */
3077 if (nb_connections
>= nb_max_connections
)
3080 /* add a new connection */
3081 c
= av_mallocz(sizeof(HTTPContext
));
3086 c
->poll_entry
= NULL
;
3087 c
->from_addr
= *from_addr
;
3088 c
->buffer_size
= IOBUFFER_INIT_SIZE
;
3089 c
->buffer
= av_malloc(c
->buffer_size
);
3094 av_strlcpy(c
->session_id
, session_id
, sizeof(c
->session_id
));
3095 c
->state
= HTTPSTATE_READY
;
3096 c
->is_packetized
= 1;
3097 c
->rtp_protocol
= rtp_protocol
;
3099 /* protocol is shown in statistics */
3100 switch(c
->rtp_protocol
) {
3101 case RTSP_PROTOCOL_RTP_UDP_MULTICAST
:
3102 proto_str
= "MCAST";
3104 case RTSP_PROTOCOL_RTP_UDP
:
3107 case RTSP_PROTOCOL_RTP_TCP
:
3114 av_strlcpy(c
->protocol
, "RTP/", sizeof(c
->protocol
));
3115 av_strlcat(c
->protocol
, proto_str
, sizeof(c
->protocol
));
3117 current_bandwidth
+= stream
->bandwidth
;
3119 c
->next
= first_http_ctx
;
3131 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3132 command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
3134 static int rtp_new_av_stream(HTTPContext
*c
,
3135 int stream_index
, struct sockaddr_in
*dest_addr
,
3136 HTTPContext
*rtsp_c
)
3138 AVFormatContext
*ctx
;
3141 URLContext
*h
= NULL
;
3143 int max_packet_size
;
3145 /* now we can open the relevant output stream */
3146 ctx
= av_alloc_format_context();
3149 ctx
->oformat
= guess_format("rtp", NULL
, NULL
);
3151 st
= av_mallocz(sizeof(AVStream
));
3154 st
->codec
= avcodec_alloc_context();
3155 ctx
->nb_streams
= 1;
3156 ctx
->streams
[0] = st
;
3158 if (!c
->stream
->feed
||
3159 c
->stream
->feed
== c
->stream
)
3160 memcpy(st
, c
->stream
->streams
[stream_index
], sizeof(AVStream
));
3163 c
->stream
->feed
->streams
[c
->stream
->feed_streams
[stream_index
]],
3165 st
->priv_data
= NULL
;
3167 /* build destination RTP address */
3168 ipaddr
= inet_ntoa(dest_addr
->sin_addr
);
3170 switch(c
->rtp_protocol
) {
3171 case RTSP_PROTOCOL_RTP_UDP
:
3172 case RTSP_PROTOCOL_RTP_UDP_MULTICAST
:
3175 /* XXX: also pass as parameter to function ? */
3176 if (c
->stream
->is_multicast
) {
3178 ttl
= c
->stream
->multicast_ttl
;
3181 snprintf(ctx
->filename
, sizeof(ctx
->filename
),
3182 "rtp://%s:%d?multicast=1&ttl=%d",
3183 ipaddr
, ntohs(dest_addr
->sin_port
), ttl
);
3185 snprintf(ctx
->filename
, sizeof(ctx
->filename
),
3186 "rtp://%s:%d", ipaddr
, ntohs(dest_addr
->sin_port
));
3189 if (url_open(&h
, ctx
->filename
, URL_WRONLY
) < 0)
3191 c
->rtp_handles
[stream_index
] = h
;
3192 max_packet_size
= url_get_max_packet_size(h
);
3194 case RTSP_PROTOCOL_RTP_TCP
:
3197 max_packet_size
= RTSP_TCP_MAX_PACKET_SIZE
;
3203 http_log("%s:%d - - \"PLAY %s/streamid=%d %s\"\n",
3204 ipaddr
, ntohs(dest_addr
->sin_port
),
3205 c
->stream
->filename
, stream_index
, c
->protocol
);
3207 /* normally, no packets should be output here, but the packet size may be checked */
3208 if (url_open_dyn_packet_buf(&ctx
->pb
, max_packet_size
) < 0) {
3209 /* XXX: close stream */
3212 av_set_parameters(ctx
, NULL
);
3213 if (av_write_header(ctx
) < 0) {
3220 url_close_dyn_buf(ctx
->pb
, &dummy_buf
);
3223 c
->rtp_ctx
[stream_index
] = ctx
;
3227 /********************************************************************/
3228 /* ffserver initialization */
3230 static AVStream
*add_av_stream1(FFStream
*stream
, AVCodecContext
*codec
)
3234 fst
= av_mallocz(sizeof(AVStream
));
3237 fst
->codec
= avcodec_alloc_context();
3238 fst
->priv_data
= av_mallocz(sizeof(FeedData
));
3239 memcpy(fst
->codec
, codec
, sizeof(AVCodecContext
));
3240 fst
->index
= stream
->nb_streams
;
3241 av_set_pts_info(fst
, 33, 1, 90000);
3242 stream
->streams
[stream
->nb_streams
++] = fst
;
3246 /* return the stream number in the feed */
3247 static int add_av_stream(FFStream
*feed
, AVStream
*st
)
3250 AVCodecContext
*av
, *av1
;
3254 for(i
=0;i
<feed
->nb_streams
;i
++) {
3255 st
= feed
->streams
[i
];
3257 if (av1
->codec_id
== av
->codec_id
&&
3258 av1
->codec_type
== av
->codec_type
&&
3259 av1
->bit_rate
== av
->bit_rate
) {
3261 switch(av
->codec_type
) {
3262 case CODEC_TYPE_AUDIO
:
3263 if (av1
->channels
== av
->channels
&&
3264 av1
->sample_rate
== av
->sample_rate
)
3267 case CODEC_TYPE_VIDEO
:
3268 if (av1
->width
== av
->width
&&
3269 av1
->height
== av
->height
&&
3270 av1
->time_base
.den
== av
->time_base
.den
&&
3271 av1
->time_base
.num
== av
->time_base
.num
&&
3272 av1
->gop_size
== av
->gop_size
)
3281 fst
= add_av_stream1(feed
, av
);
3284 return feed
->nb_streams
- 1;
3289 static void remove_stream(FFStream
*stream
)
3293 while (*ps
!= NULL
) {
3301 /* specific mpeg4 handling : we extract the raw parameters */
3302 static void extract_mpeg4_header(AVFormatContext
*infile
)
3304 int mpeg4_count
, i
, size
;
3310 for(i
=0;i
<infile
->nb_streams
;i
++) {
3311 st
= infile
->streams
[i
];
3312 if (st
->codec
->codec_id
== CODEC_ID_MPEG4
&&
3313 st
->codec
->extradata_size
== 0) {
3320 printf("MPEG4 without extra data: trying to find header in %s\n", infile
->filename
);
3321 while (mpeg4_count
> 0) {
3322 if (av_read_packet(infile
, &pkt
) < 0)
3324 st
= infile
->streams
[pkt
.stream_index
];
3325 if (st
->codec
->codec_id
== CODEC_ID_MPEG4
&&
3326 st
->codec
->extradata_size
== 0) {
3327 av_freep(&st
->codec
->extradata
);
3328 /* fill extradata with the header */
3329 /* XXX: we make hard suppositions here ! */
3331 while (p
< pkt
.data
+ pkt
.size
- 4) {
3332 /* stop when vop header is found */
3333 if (p
[0] == 0x00 && p
[1] == 0x00 &&
3334 p
[2] == 0x01 && p
[3] == 0xb6) {
3335 size
= p
- pkt
.data
;
3336 // av_hex_dump_log(infile, AV_LOG_DEBUG, pkt.data, size);
3337 st
->codec
->extradata
= av_malloc(size
);
3338 st
->codec
->extradata_size
= size
;
3339 memcpy(st
->codec
->extradata
, pkt
.data
, size
);
3346 av_free_packet(&pkt
);
3350 /* compute the needed AVStream for each file */
3351 static void build_file_streams(void)
3353 FFStream
*stream
, *stream_next
;
3354 AVFormatContext
*infile
;
3357 /* gather all streams */
3358 for(stream
= first_stream
; stream
!= NULL
; stream
= stream_next
) {
3359 stream_next
= stream
->next
;
3360 if (stream
->stream_type
== STREAM_TYPE_LIVE
&&
3362 /* the stream comes from a file */
3363 /* try to open the file */
3365 stream
->ap_in
= av_mallocz(sizeof(AVFormatParameters
));
3366 if (stream
->fmt
&& !strcmp(stream
->fmt
->name
, "rtp")) {
3367 /* specific case : if transport stream output to RTP,
3368 we use a raw transport stream reader */
3369 stream
->ap_in
->mpeg2ts_raw
= 1;
3370 stream
->ap_in
->mpeg2ts_compute_pcr
= 1;
3373 if ((ret
= av_open_input_file(&infile
, stream
->feed_filename
,
3374 stream
->ifmt
, 0, stream
->ap_in
)) < 0) {
3375 http_log("could not open %s: %d\n", stream
->feed_filename
, ret
);
3376 /* remove stream (no need to spend more time on it) */
3378 remove_stream(stream
);
3380 /* find all the AVStreams inside and reference them in
3382 if (av_find_stream_info(infile
) < 0) {
3383 http_log("Could not find codec parameters from '%s'\n",
3384 stream
->feed_filename
);
3385 av_close_input_file(infile
);
3388 extract_mpeg4_header(infile
);
3390 for(i
=0;i
<infile
->nb_streams
;i
++)
3391 add_av_stream1(stream
, infile
->streams
[i
]->codec
);
3393 av_close_input_file(infile
);
3399 /* compute the needed AVStream for each feed */
3400 static void build_feed_streams(void)
3402 FFStream
*stream
, *feed
;
3405 /* gather all streams */
3406 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
3407 feed
= stream
->feed
;
3409 if (!stream
->is_feed
) {
3410 /* we handle a stream coming from a feed */
3411 for(i
=0;i
<stream
->nb_streams
;i
++)
3412 stream
->feed_streams
[i
] = add_av_stream(feed
, stream
->streams
[i
]);
3417 /* gather all streams */
3418 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
3419 feed
= stream
->feed
;
3421 if (stream
->is_feed
) {
3422 for(i
=0;i
<stream
->nb_streams
;i
++)
3423 stream
->feed_streams
[i
] = i
;
3428 /* create feed files if needed */
3429 for(feed
= first_feed
; feed
!= NULL
; feed
= feed
->next_feed
) {
3432 if (url_exist(feed
->feed_filename
)) {
3433 /* See if it matches */
3437 if (av_open_input_file(&s
, feed
->feed_filename
, NULL
, FFM_PACKET_SIZE
, NULL
) >= 0) {
3438 /* Now see if it matches */
3439 if (s
->nb_streams
== feed
->nb_streams
) {
3441 for(i
=0;i
<s
->nb_streams
;i
++) {
3443 sf
= feed
->streams
[i
];
3446 if (sf
->index
!= ss
->index
||
3448 http_log("Index & Id do not match for stream %d (%s)\n",
3449 i
, feed
->feed_filename
);
3452 AVCodecContext
*ccf
, *ccs
;
3456 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3458 if (CHECK_CODEC(codec
) || CHECK_CODEC(codec_type
)) {
3459 http_log("Codecs do not match for stream %d\n", i
);
3461 } else if (CHECK_CODEC(bit_rate
) || CHECK_CODEC(flags
)) {
3462 http_log("Codec bitrates do not match for stream %d\n", i
);
3464 } else if (ccf
->codec_type
== CODEC_TYPE_VIDEO
) {
3465 if (CHECK_CODEC(time_base
.den
) ||
3466 CHECK_CODEC(time_base
.num
) ||
3467 CHECK_CODEC(width
) ||
3468 CHECK_CODEC(height
)) {
3469 http_log("Codec width, height and framerate do not match for stream %d\n", i
);
3472 } else if (ccf
->codec_type
== CODEC_TYPE_AUDIO
) {
3473 if (CHECK_CODEC(sample_rate
) ||
3474 CHECK_CODEC(channels
) ||
3475 CHECK_CODEC(frame_size
)) {
3476 http_log("Codec sample_rate, channels, frame_size do not match for stream %d\n", i
);
3480 http_log("Unknown codec type\n");
3488 http_log("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3489 feed
->feed_filename
, s
->nb_streams
, feed
->nb_streams
);
3491 av_close_input_file(s
);
3493 http_log("Deleting feed file '%s' as it appears to be corrupt\n",
3494 feed
->feed_filename
);
3497 if (feed
->readonly
) {
3498 http_log("Unable to delete feed file '%s' as it is marked readonly\n",
3499 feed
->feed_filename
);
3502 unlink(feed
->feed_filename
);
3505 if (!url_exist(feed
->feed_filename
)) {
3506 AVFormatContext s1
, *s
= &s1
;
3508 if (feed
->readonly
) {
3509 http_log("Unable to create feed file '%s' as it is marked readonly\n",
3510 feed
->feed_filename
);
3514 /* only write the header of the ffm file */
3515 if (url_fopen(&s
->pb
, feed
->feed_filename
, URL_WRONLY
) < 0) {
3516 http_log("Could not open output feed file '%s'\n",
3517 feed
->feed_filename
);
3520 s
->oformat
= feed
->fmt
;
3521 s
->nb_streams
= feed
->nb_streams
;
3522 for(i
=0;i
<s
->nb_streams
;i
++) {
3524 st
= feed
->streams
[i
];
3527 av_set_parameters(s
, NULL
);
3528 if (av_write_header(s
) < 0) {
3529 http_log("Container doesn't supports the required parameters\n");
3532 /* XXX: need better api */
3533 av_freep(&s
->priv_data
);
3536 /* get feed size and write index */
3537 fd
= open(feed
->feed_filename
, O_RDONLY
);
3539 http_log("Could not open output feed file '%s'\n",
3540 feed
->feed_filename
);
3544 feed
->feed_write_index
= ffm_read_write_index(fd
);
3545 feed
->feed_size
= lseek(fd
, 0, SEEK_END
);
3546 /* ensure that we do not wrap before the end of file */
3547 if (feed
->feed_max_size
&& feed
->feed_max_size
< feed
->feed_size
)
3548 feed
->feed_max_size
= feed
->feed_size
;
3554 /* compute the bandwidth used by each stream */
3555 static void compute_bandwidth(void)
3561 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
3563 for(i
=0;i
<stream
->nb_streams
;i
++) {
3564 AVStream
*st
= stream
->streams
[i
];
3565 switch(st
->codec
->codec_type
) {
3566 case CODEC_TYPE_AUDIO
:
3567 case CODEC_TYPE_VIDEO
:
3568 bandwidth
+= st
->codec
->bit_rate
;
3574 stream
->bandwidth
= (bandwidth
+ 999) / 1000;
3578 static void get_arg(char *buf
, int buf_size
, const char **pp
)
3585 while (isspace(*p
)) p
++;
3588 if (*p
== '\"' || *p
== '\'')
3600 if ((q
- buf
) < buf_size
- 1)
3605 if (quote
&& *p
== quote
)
3610 /* add a codec and set the default parameters */
3611 static void add_codec(FFStream
*stream
, AVCodecContext
*av
)
3615 /* compute default parameters */
3616 switch(av
->codec_type
) {
3617 case CODEC_TYPE_AUDIO
:
3618 if (av
->bit_rate
== 0)
3619 av
->bit_rate
= 64000;
3620 if (av
->sample_rate
== 0)
3621 av
->sample_rate
= 22050;
3622 if (av
->channels
== 0)
3625 case CODEC_TYPE_VIDEO
:
3626 if (av
->bit_rate
== 0)
3627 av
->bit_rate
= 64000;
3628 if (av
->time_base
.num
== 0){
3629 av
->time_base
.den
= 5;
3630 av
->time_base
.num
= 1;
3632 if (av
->width
== 0 || av
->height
== 0) {
3636 /* Bitrate tolerance is less for streaming */
3637 if (av
->bit_rate_tolerance
== 0)
3638 av
->bit_rate_tolerance
= FFMAX(av
->bit_rate
/ 4,
3639 (int64_t)av
->bit_rate
*av
->time_base
.num
/av
->time_base
.den
);
3644 if (av
->max_qdiff
== 0)
3646 av
->qcompress
= 0.5;
3649 if (!av
->nsse_weight
)
3650 av
->nsse_weight
= 8;
3652 av
->frame_skip_cmp
= FF_CMP_DCTMAX
;
3653 av
->me_method
= ME_EPZS
;
3654 av
->rc_buffer_aggressivity
= 1.0;
3657 av
->rc_eq
= "tex^qComp";
3658 if (!av
->i_quant_factor
)
3659 av
->i_quant_factor
= -0.8;
3660 if (!av
->b_quant_factor
)
3661 av
->b_quant_factor
= 1.25;
3662 if (!av
->b_quant_offset
)
3663 av
->b_quant_offset
= 1.25;
3664 if (!av
->rc_max_rate
)
3665 av
->rc_max_rate
= av
->bit_rate
* 2;
3667 if (av
->rc_max_rate
&& !av
->rc_buffer_size
) {
3668 av
->rc_buffer_size
= av
->rc_max_rate
;
3677 st
= av_mallocz(sizeof(AVStream
));
3680 st
->codec
= avcodec_alloc_context();
3681 stream
->streams
[stream
->nb_streams
++] = st
;
3682 memcpy(st
->codec
, av
, sizeof(AVCodecContext
));
3685 static int opt_audio_codec(const char *arg
)
3687 AVCodec
*p
= avcodec_find_encoder_by_name(arg
);
3689 if (p
== NULL
|| p
->type
!= CODEC_TYPE_AUDIO
)
3690 return CODEC_ID_NONE
;
3695 static int opt_video_codec(const char *arg
)
3697 AVCodec
*p
= avcodec_find_encoder_by_name(arg
);
3699 if (p
== NULL
|| p
->type
!= CODEC_TYPE_VIDEO
)
3700 return CODEC_ID_NONE
;
3705 /* simplistic plugin support */
3708 static void load_module(const char *filename
)
3711 void (*init_func
)(void);
3712 dll
= dlopen(filename
, RTLD_NOW
);
3714 fprintf(stderr
, "Could not load module '%s' - %s\n",
3715 filename
, dlerror());
3719 init_func
= dlsym(dll
, "ffserver_module_init");
3722 "%s: init function 'ffserver_module_init()' not found\n",
3731 static int opt_default(const char *opt
, const char *arg
,
3732 AVCodecContext
*avctx
, int type
)
3734 const AVOption
*o
= NULL
;
3735 const AVOption
*o2
= av_find_opt(avctx
, opt
, NULL
, type
, type
);
3737 o
= av_set_string2(avctx
, opt
, arg
, 1);
3743 static int parse_ffconfig(const char *filename
)
3750 int val
, errors
, line_num
;
3751 FFStream
**last_stream
, *stream
, *redirect
;
3752 FFStream
**last_feed
, *feed
;
3753 AVCodecContext audio_enc
, video_enc
;
3754 int audio_id
, video_id
;
3756 f
= fopen(filename
, "r");
3764 first_stream
= NULL
;
3765 last_stream
= &first_stream
;
3767 last_feed
= &first_feed
;
3771 audio_id
= CODEC_ID_NONE
;
3772 video_id
= CODEC_ID_NONE
;
3774 if (fgets(line
, sizeof(line
), f
) == NULL
)
3780 if (*p
== '\0' || *p
== '#')
3783 get_arg(cmd
, sizeof(cmd
), &p
);
3785 if (!strcasecmp(cmd
, "Port")) {
3786 get_arg(arg
, sizeof(arg
), &p
);
3788 if (val
< 1 || val
> 65536) {
3789 fprintf(stderr
, "%s:%d: Invalid port: %s\n",
3790 filename
, line_num
, arg
);
3793 my_http_addr
.sin_port
= htons(val
);
3794 } else if (!strcasecmp(cmd
, "BindAddress")) {
3795 get_arg(arg
, sizeof(arg
), &p
);
3796 if (resolve_host(&my_http_addr
.sin_addr
, arg
) != 0) {
3797 fprintf(stderr
, "%s:%d: Invalid host/IP address: %s\n",
3798 filename
, line_num
, arg
);
3801 } else if (!strcasecmp(cmd
, "NoDaemon")) {
3802 ffserver_daemon
= 0;
3803 } else if (!strcasecmp(cmd
, "RTSPPort")) {
3804 get_arg(arg
, sizeof(arg
), &p
);
3806 if (val
< 1 || val
> 65536) {
3807 fprintf(stderr
, "%s:%d: Invalid port: %s\n",
3808 filename
, line_num
, arg
);
3811 my_rtsp_addr
.sin_port
= htons(atoi(arg
));
3812 } else if (!strcasecmp(cmd
, "RTSPBindAddress")) {
3813 get_arg(arg
, sizeof(arg
), &p
);
3814 if (resolve_host(&my_rtsp_addr
.sin_addr
, arg
) != 0) {
3815 fprintf(stderr
, "%s:%d: Invalid host/IP address: %s\n",
3816 filename
, line_num
, arg
);
3819 } else if (!strcasecmp(cmd
, "MaxClients")) {
3820 get_arg(arg
, sizeof(arg
), &p
);
3822 if (val
< 1 || val
> HTTP_MAX_CONNECTIONS
) {
3823 fprintf(stderr
, "%s:%d: Invalid MaxClients: %s\n",
3824 filename
, line_num
, arg
);
3827 nb_max_connections
= val
;
3829 } else if (!strcasecmp(cmd
, "MaxBandwidth")) {
3831 get_arg(arg
, sizeof(arg
), &p
);
3833 if (llval
< 10 || llval
> 10000000) {
3834 fprintf(stderr
, "%s:%d: Invalid MaxBandwidth: %s\n",
3835 filename
, line_num
, arg
);
3838 max_bandwidth
= llval
;
3839 } else if (!strcasecmp(cmd
, "CustomLog")) {
3840 if (!ffserver_debug
)
3841 get_arg(logfilename
, sizeof(logfilename
), &p
);
3842 } else if (!strcasecmp(cmd
, "<Feed")) {
3843 /*********************************************/
3844 /* Feed related options */
3846 if (stream
|| feed
) {
3847 fprintf(stderr
, "%s:%d: Already in a tag\n",
3848 filename
, line_num
);
3850 feed
= av_mallocz(sizeof(FFStream
));
3851 /* add in stream list */
3852 *last_stream
= feed
;
3853 last_stream
= &feed
->next
;
3854 /* add in feed list */
3856 last_feed
= &feed
->next_feed
;
3858 get_arg(feed
->filename
, sizeof(feed
->filename
), &p
);
3859 q
= strrchr(feed
->filename
, '>');
3862 feed
->fmt
= guess_format("ffm", NULL
, NULL
);
3863 /* defaut feed file */
3864 snprintf(feed
->feed_filename
, sizeof(feed
->feed_filename
),
3865 "/tmp/%s.ffm", feed
->filename
);
3866 feed
->feed_max_size
= 5 * 1024 * 1024;
3868 feed
->feed
= feed
; /* self feeding :-) */
3870 } else if (!strcasecmp(cmd
, "Launch")) {
3874 feed
->child_argv
= av_mallocz(64 * sizeof(char *));
3876 for (i
= 0; i
< 62; i
++) {
3877 get_arg(arg
, sizeof(arg
), &p
);
3881 feed
->child_argv
[i
] = av_strdup(arg
);
3884 feed
->child_argv
[i
] = av_malloc(30 + strlen(feed
->filename
));
3886 snprintf(feed
->child_argv
[i
], 30+strlen(feed
->filename
),
3888 (my_http_addr
.sin_addr
.s_addr
== INADDR_ANY
) ? "127.0.0.1" :
3889 inet_ntoa(my_http_addr
.sin_addr
),
3890 ntohs(my_http_addr
.sin_port
), feed
->filename
);
3892 } else if (!strcasecmp(cmd
, "ReadOnlyFile")) {
3894 get_arg(feed
->feed_filename
, sizeof(feed
->feed_filename
), &p
);
3896 } else if (stream
) {
3897 get_arg(stream
->feed_filename
, sizeof(stream
->feed_filename
), &p
);
3899 } else if (!strcasecmp(cmd
, "File")) {
3901 get_arg(feed
->feed_filename
, sizeof(feed
->feed_filename
), &p
);
3903 get_arg(stream
->feed_filename
, sizeof(stream
->feed_filename
), &p
);
3904 } else if (!strcasecmp(cmd
, "FileMaxSize")) {
3909 get_arg(arg
, sizeof(arg
), &p
);
3911 fsize
= strtod(p1
, &p1
);
3912 switch(toupper(*p1
)) {
3917 fsize
*= 1024 * 1024;
3920 fsize
*= 1024 * 1024 * 1024;
3923 feed
->feed_max_size
= (int64_t)fsize
;
3925 } else if (!strcasecmp(cmd
, "</Feed>")) {
3927 fprintf(stderr
, "%s:%d: No corresponding <Feed> for </Feed>\n",
3928 filename
, line_num
);
3932 } else if (!strcasecmp(cmd
, "<Stream")) {
3933 /*********************************************/
3934 /* Stream related options */
3936 if (stream
|| feed
) {
3937 fprintf(stderr
, "%s:%d: Already in a tag\n",
3938 filename
, line_num
);
3940 const AVClass
*class;
3941 stream
= av_mallocz(sizeof(FFStream
));
3942 *last_stream
= stream
;
3943 last_stream
= &stream
->next
;
3945 get_arg(stream
->filename
, sizeof(stream
->filename
), &p
);
3946 q
= strrchr(stream
->filename
, '>');
3949 stream
->fmt
= guess_stream_format(NULL
, stream
->filename
, NULL
);
3950 /* fetch avclass so AVOption works
3951 * FIXME try to use avcodec_get_context_defaults2
3952 * without changing defaults too much */
3953 avcodec_get_context_defaults(&video_enc
);
3954 class = video_enc
.av_class
;
3955 memset(&audio_enc
, 0, sizeof(AVCodecContext
));
3956 memset(&video_enc
, 0, sizeof(AVCodecContext
));
3957 audio_enc
.av_class
= class;
3958 video_enc
.av_class
= class;
3959 audio_id
= CODEC_ID_NONE
;
3960 video_id
= CODEC_ID_NONE
;
3962 audio_id
= stream
->fmt
->audio_codec
;
3963 video_id
= stream
->fmt
->video_codec
;
3966 } else if (!strcasecmp(cmd
, "Feed")) {
3967 get_arg(arg
, sizeof(arg
), &p
);
3972 while (sfeed
!= NULL
) {
3973 if (!strcmp(sfeed
->filename
, arg
))
3975 sfeed
= sfeed
->next_feed
;
3978 fprintf(stderr
, "%s:%d: feed '%s' not defined\n",
3979 filename
, line_num
, arg
);
3981 stream
->feed
= sfeed
;
3983 } else if (!strcasecmp(cmd
, "Format")) {
3984 get_arg(arg
, sizeof(arg
), &p
);
3986 if (!strcmp(arg
, "status")) {
3987 stream
->stream_type
= STREAM_TYPE_STATUS
;
3990 stream
->stream_type
= STREAM_TYPE_LIVE
;
3991 /* jpeg cannot be used here, so use single frame jpeg */
3992 if (!strcmp(arg
, "jpeg"))
3993 strcpy(arg
, "mjpeg");
3994 stream
->fmt
= guess_stream_format(arg
, NULL
, NULL
);
3996 fprintf(stderr
, "%s:%d: Unknown Format: %s\n",
3997 filename
, line_num
, arg
);
4002 audio_id
= stream
->fmt
->audio_codec
;
4003 video_id
= stream
->fmt
->video_codec
;
4006 } else if (!strcasecmp(cmd
, "InputFormat")) {
4007 get_arg(arg
, sizeof(arg
), &p
);
4008 stream
->ifmt
= av_find_input_format(arg
);
4009 if (!stream
->ifmt
) {
4010 fprintf(stderr
, "%s:%d: Unknown input format: %s\n",
4011 filename
, line_num
, arg
);
4013 } else if (!strcasecmp(cmd
, "FaviconURL")) {
4014 if (stream
&& stream
->stream_type
== STREAM_TYPE_STATUS
) {
4015 get_arg(stream
->feed_filename
, sizeof(stream
->feed_filename
), &p
);
4017 fprintf(stderr
, "%s:%d: FaviconURL only permitted for status streams\n",
4018 filename
, line_num
);
4021 } else if (!strcasecmp(cmd
, "Author")) {
4023 get_arg(stream
->author
, sizeof(stream
->author
), &p
);
4024 } else if (!strcasecmp(cmd
, "Comment")) {
4026 get_arg(stream
->comment
, sizeof(stream
->comment
), &p
);
4027 } else if (!strcasecmp(cmd
, "Copyright")) {
4029 get_arg(stream
->copyright
, sizeof(stream
->copyright
), &p
);
4030 } else if (!strcasecmp(cmd
, "Title")) {
4032 get_arg(stream
->title
, sizeof(stream
->title
), &p
);
4033 } else if (!strcasecmp(cmd
, "Preroll")) {
4034 get_arg(arg
, sizeof(arg
), &p
);
4036 stream
->prebuffer
= atof(arg
) * 1000;
4037 } else if (!strcasecmp(cmd
, "StartSendOnKey")) {
4039 stream
->send_on_key
= 1;
4040 } else if (!strcasecmp(cmd
, "AudioCodec")) {
4041 get_arg(arg
, sizeof(arg
), &p
);
4042 audio_id
= opt_audio_codec(arg
);
4043 if (audio_id
== CODEC_ID_NONE
) {
4044 fprintf(stderr
, "%s:%d: Unknown AudioCodec: %s\n",
4045 filename
, line_num
, arg
);
4048 } else if (!strcasecmp(cmd
, "VideoCodec")) {
4049 get_arg(arg
, sizeof(arg
), &p
);
4050 video_id
= opt_video_codec(arg
);
4051 if (video_id
== CODEC_ID_NONE
) {
4052 fprintf(stderr
, "%s:%d: Unknown VideoCodec: %s\n",
4053 filename
, line_num
, arg
);
4056 } else if (!strcasecmp(cmd
, "MaxTime")) {
4057 get_arg(arg
, sizeof(arg
), &p
);
4059 stream
->max_time
= atof(arg
) * 1000;
4060 } else if (!strcasecmp(cmd
, "AudioBitRate")) {
4061 get_arg(arg
, sizeof(arg
), &p
);
4063 audio_enc
.bit_rate
= atoi(arg
) * 1000;
4064 } else if (!strcasecmp(cmd
, "AudioChannels")) {
4065 get_arg(arg
, sizeof(arg
), &p
);
4067 audio_enc
.channels
= atoi(arg
);
4068 } else if (!strcasecmp(cmd
, "AudioSampleRate")) {
4069 get_arg(arg
, sizeof(arg
), &p
);
4071 audio_enc
.sample_rate
= atoi(arg
);
4072 } else if (!strcasecmp(cmd
, "AudioQuality")) {
4073 get_arg(arg
, sizeof(arg
), &p
);
4075 // audio_enc.quality = atof(arg) * 1000;
4077 } else if (!strcasecmp(cmd
, "VideoBitRateRange")) {
4079 int minrate
, maxrate
;
4081 get_arg(arg
, sizeof(arg
), &p
);
4083 if (sscanf(arg
, "%d-%d", &minrate
, &maxrate
) == 2) {
4084 video_enc
.rc_min_rate
= minrate
* 1000;
4085 video_enc
.rc_max_rate
= maxrate
* 1000;
4087 fprintf(stderr
, "%s:%d: Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n",
4088 filename
, line_num
, arg
);
4092 } else if (!strcasecmp(cmd
, "Debug")) {
4094 get_arg(arg
, sizeof(arg
), &p
);
4095 video_enc
.debug
= strtol(arg
,0,0);
4097 } else if (!strcasecmp(cmd
, "Strict")) {
4099 get_arg(arg
, sizeof(arg
), &p
);
4100 video_enc
.strict_std_compliance
= atoi(arg
);
4102 } else if (!strcasecmp(cmd
, "VideoBufferSize")) {
4104 get_arg(arg
, sizeof(arg
), &p
);
4105 video_enc
.rc_buffer_size
= atoi(arg
) * 8*1024;
4107 } else if (!strcasecmp(cmd
, "VideoBitRateTolerance")) {
4109 get_arg(arg
, sizeof(arg
), &p
);
4110 video_enc
.bit_rate_tolerance
= atoi(arg
) * 1000;
4112 } else if (!strcasecmp(cmd
, "VideoBitRate")) {
4113 get_arg(arg
, sizeof(arg
), &p
);
4115 video_enc
.bit_rate
= atoi(arg
) * 1000;
4117 } else if (!strcasecmp(cmd
, "VideoSize")) {
4118 get_arg(arg
, sizeof(arg
), &p
);
4120 av_parse_video_frame_size(&video_enc
.width
, &video_enc
.height
, arg
);
4121 if ((video_enc
.width
% 16) != 0 ||
4122 (video_enc
.height
% 16) != 0) {
4123 fprintf(stderr
, "%s:%d: Image size must be a multiple of 16\n",
4124 filename
, line_num
);
4128 } else if (!strcasecmp(cmd
, "VideoFrameRate")) {
4129 get_arg(arg
, sizeof(arg
), &p
);
4131 AVRational frame_rate
;
4132 if (av_parse_video_frame_rate(&frame_rate
, arg
) < 0) {
4133 fprintf(stderr
, "Incorrect frame rate\n");
4136 video_enc
.time_base
.num
= frame_rate
.den
;
4137 video_enc
.time_base
.den
= frame_rate
.num
;
4140 } else if (!strcasecmp(cmd
, "VideoGopSize")) {
4141 get_arg(arg
, sizeof(arg
), &p
);
4143 video_enc
.gop_size
= atoi(arg
);
4144 } else if (!strcasecmp(cmd
, "VideoIntraOnly")) {
4146 video_enc
.gop_size
= 1;
4147 } else if (!strcasecmp(cmd
, "VideoHighQuality")) {
4149 video_enc
.mb_decision
= FF_MB_DECISION_BITS
;
4150 } else if (!strcasecmp(cmd
, "Video4MotionVector")) {
4152 video_enc
.mb_decision
= FF_MB_DECISION_BITS
; //FIXME remove
4153 video_enc
.flags
|= CODEC_FLAG_4MV
;
4155 } else if (!strcasecmp(cmd
, "AVOptionVideo") ||
4156 !strcasecmp(cmd
, "AVOptionAudio")) {
4158 AVCodecContext
*avctx
;
4160 get_arg(arg
, sizeof(arg
), &p
);
4161 get_arg(arg2
, sizeof(arg2
), &p
);
4162 if (!strcasecmp(cmd
, "AVOptionVideo")) {
4164 type
= AV_OPT_FLAG_VIDEO_PARAM
;
4167 type
= AV_OPT_FLAG_AUDIO_PARAM
;
4169 if (opt_default(arg
, arg2
, avctx
, type
|AV_OPT_FLAG_ENCODING_PARAM
)) {
4170 fprintf(stderr
, "AVOption error: %s %s\n", arg
, arg2
);
4173 } else if (!strcasecmp(cmd
, "VideoTag")) {
4174 get_arg(arg
, sizeof(arg
), &p
);
4175 if ((strlen(arg
) == 4) && stream
)
4176 video_enc
.codec_tag
= ff_get_fourcc(arg
);
4177 } else if (!strcasecmp(cmd
, "BitExact")) {
4179 video_enc
.flags
|= CODEC_FLAG_BITEXACT
;
4180 } else if (!strcasecmp(cmd
, "DctFastint")) {
4182 video_enc
.dct_algo
= FF_DCT_FASTINT
;
4183 } else if (!strcasecmp(cmd
, "IdctSimple")) {
4185 video_enc
.idct_algo
= FF_IDCT_SIMPLE
;
4186 } else if (!strcasecmp(cmd
, "Qscale")) {
4187 get_arg(arg
, sizeof(arg
), &p
);
4189 video_enc
.flags
|= CODEC_FLAG_QSCALE
;
4190 video_enc
.global_quality
= FF_QP2LAMBDA
* atoi(arg
);
4192 } else if (!strcasecmp(cmd
, "VideoQDiff")) {
4193 get_arg(arg
, sizeof(arg
), &p
);
4195 video_enc
.max_qdiff
= atoi(arg
);
4196 if (video_enc
.max_qdiff
< 1 || video_enc
.max_qdiff
> 31) {
4197 fprintf(stderr
, "%s:%d: VideoQDiff out of range\n",
4198 filename
, line_num
);
4202 } else if (!strcasecmp(cmd
, "VideoQMax")) {
4203 get_arg(arg
, sizeof(arg
), &p
);
4205 video_enc
.qmax
= atoi(arg
);
4206 if (video_enc
.qmax
< 1 || video_enc
.qmax
> 31) {
4207 fprintf(stderr
, "%s:%d: VideoQMax out of range\n",
4208 filename
, line_num
);
4212 } else if (!strcasecmp(cmd
, "VideoQMin")) {
4213 get_arg(arg
, sizeof(arg
), &p
);
4215 video_enc
.qmin
= atoi(arg
);
4216 if (video_enc
.qmin
< 1 || video_enc
.qmin
> 31) {
4217 fprintf(stderr
, "%s:%d: VideoQMin out of range\n",
4218 filename
, line_num
);
4222 } else if (!strcasecmp(cmd
, "LumaElim")) {
4223 get_arg(arg
, sizeof(arg
), &p
);
4225 video_enc
.luma_elim_threshold
= atoi(arg
);
4226 } else if (!strcasecmp(cmd
, "ChromaElim")) {
4227 get_arg(arg
, sizeof(arg
), &p
);
4229 video_enc
.chroma_elim_threshold
= atoi(arg
);
4230 } else if (!strcasecmp(cmd
, "LumiMask")) {
4231 get_arg(arg
, sizeof(arg
), &p
);
4233 video_enc
.lumi_masking
= atof(arg
);
4234 } else if (!strcasecmp(cmd
, "DarkMask")) {
4235 get_arg(arg
, sizeof(arg
), &p
);
4237 video_enc
.dark_masking
= atof(arg
);
4238 } else if (!strcasecmp(cmd
, "NoVideo")) {
4239 video_id
= CODEC_ID_NONE
;
4240 } else if (!strcasecmp(cmd
, "NoAudio")) {
4241 audio_id
= CODEC_ID_NONE
;
4242 } else if (!strcasecmp(cmd
, "ACL")) {
4245 get_arg(arg
, sizeof(arg
), &p
);
4246 if (strcasecmp(arg
, "allow") == 0)
4247 acl
.action
= IP_ALLOW
;
4248 else if (strcasecmp(arg
, "deny") == 0)
4249 acl
.action
= IP_DENY
;
4251 fprintf(stderr
, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
4252 filename
, line_num
, arg
);
4256 get_arg(arg
, sizeof(arg
), &p
);
4258 if (resolve_host(&acl
.first
, arg
) != 0) {
4259 fprintf(stderr
, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4260 filename
, line_num
, arg
);
4263 acl
.last
= acl
.first
;
4265 get_arg(arg
, sizeof(arg
), &p
);
4268 if (resolve_host(&acl
.last
, arg
) != 0) {
4269 fprintf(stderr
, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4270 filename
, line_num
, arg
);
4276 IPAddressACL
*nacl
= av_mallocz(sizeof(*nacl
));
4277 IPAddressACL
**naclp
= 0;
4283 naclp
= &stream
->acl
;
4287 fprintf(stderr
, "%s:%d: ACL found not in <stream> or <feed>\n",
4288 filename
, line_num
);
4294 naclp
= &(*naclp
)->next
;
4299 } else if (!strcasecmp(cmd
, "RTSPOption")) {
4300 get_arg(arg
, sizeof(arg
), &p
);
4302 av_freep(&stream
->rtsp_option
);
4303 stream
->rtsp_option
= av_strdup(arg
);
4305 } else if (!strcasecmp(cmd
, "MulticastAddress")) {
4306 get_arg(arg
, sizeof(arg
), &p
);
4308 if (resolve_host(&stream
->multicast_ip
, arg
) != 0) {
4309 fprintf(stderr
, "%s:%d: Invalid host/IP address: %s\n",
4310 filename
, line_num
, arg
);
4313 stream
->is_multicast
= 1;
4314 stream
->loop
= 1; /* default is looping */
4316 } else if (!strcasecmp(cmd
, "MulticastPort")) {
4317 get_arg(arg
, sizeof(arg
), &p
);
4319 stream
->multicast_port
= atoi(arg
);
4320 } else if (!strcasecmp(cmd
, "MulticastTTL")) {
4321 get_arg(arg
, sizeof(arg
), &p
);
4323 stream
->multicast_ttl
= atoi(arg
);
4324 } else if (!strcasecmp(cmd
, "NoLoop")) {
4327 } else if (!strcasecmp(cmd
, "</Stream>")) {
4329 fprintf(stderr
, "%s:%d: No corresponding <Stream> for </Stream>\n",
4330 filename
, line_num
);
4333 if (stream
->feed
&& stream
->fmt
&& strcmp(stream
->fmt
->name
, "ffm") != 0) {
4334 if (audio_id
!= CODEC_ID_NONE
) {
4335 audio_enc
.codec_type
= CODEC_TYPE_AUDIO
;
4336 audio_enc
.codec_id
= audio_id
;
4337 add_codec(stream
, &audio_enc
);
4339 if (video_id
!= CODEC_ID_NONE
) {
4340 video_enc
.codec_type
= CODEC_TYPE_VIDEO
;
4341 video_enc
.codec_id
= video_id
;
4342 add_codec(stream
, &video_enc
);
4347 } else if (!strcasecmp(cmd
, "<Redirect")) {
4348 /*********************************************/
4350 if (stream
|| feed
|| redirect
) {
4351 fprintf(stderr
, "%s:%d: Already in a tag\n",
4352 filename
, line_num
);
4355 redirect
= av_mallocz(sizeof(FFStream
));
4356 *last_stream
= redirect
;
4357 last_stream
= &redirect
->next
;
4359 get_arg(redirect
->filename
, sizeof(redirect
->filename
), &p
);
4360 q
= strrchr(redirect
->filename
, '>');
4363 redirect
->stream_type
= STREAM_TYPE_REDIRECT
;
4365 } else if (!strcasecmp(cmd
, "URL")) {
4367 get_arg(redirect
->feed_filename
, sizeof(redirect
->feed_filename
), &p
);
4368 } else if (!strcasecmp(cmd
, "</Redirect>")) {
4370 fprintf(stderr
, "%s:%d: No corresponding <Redirect> for </Redirect>\n",
4371 filename
, line_num
);
4374 if (!redirect
->feed_filename
[0]) {
4375 fprintf(stderr
, "%s:%d: No URL found for <Redirect>\n",
4376 filename
, line_num
);
4381 } else if (!strcasecmp(cmd
, "LoadModule")) {
4382 get_arg(arg
, sizeof(arg
), &p
);
4386 fprintf(stderr
, "%s:%d: Module support not compiled into this version: '%s'\n",
4387 filename
, line_num
, arg
);
4391 fprintf(stderr
, "%s:%d: Incorrect keyword: '%s'\n",
4392 filename
, line_num
, cmd
);
4404 static void handle_child_exit(int sig
)
4409 while ((pid
= waitpid(-1, &status
, WNOHANG
)) > 0) {
4412 for (feed
= first_feed
; feed
; feed
= feed
->next
) {
4413 if (feed
->pid
== pid
) {
4414 int uptime
= time(0) - feed
->pid_start
;
4417 fprintf(stderr
, "%s: Pid %d exited with status %d after %d seconds\n", feed
->filename
, pid
, status
, uptime
);
4420 /* Turn off any more restarts */
4421 feed
->child_argv
= 0;
4426 need_to_start_children
= 1;
4429 static void opt_debug()
4432 ffserver_daemon
= 0;
4433 logfilename
[0] = '-';
4436 static void opt_show_help(void)
4438 printf("usage: ffserver [options]\n"
4439 "Hyper fast multi format Audio/Video streaming server\n");
4441 show_help_options(options
, "Main options:\n", 0, 0);
4444 static const OptionDef options
[] = {
4445 { "h", OPT_EXIT
, {(void*)opt_show_help
}, "show help" },
4446 { "version", OPT_EXIT
, {(void*)show_version
}, "show version" },
4447 { "L", OPT_EXIT
, {(void*)show_license
}, "show license" },
4448 { "formats", OPT_EXIT
, {(void*)show_formats
}, "show available formats, codecs, protocols, ..." },
4449 { "n", OPT_BOOL
, {(void *)&no_launch
}, "enable no-launch mode" },
4450 { "d", 0, {(void*)opt_debug
}, "enable debug mode" },
4451 { "f", HAS_ARG
| OPT_STRING
, {(void*)&config_filename
}, "use configfile instead of /etc/ffserver.conf", "configfile" },
4455 int main(int argc
, char **argv
)
4457 struct sigaction sigact
;
4463 config_filename
= "/etc/ffserver.conf";
4465 my_program_name
= argv
[0];
4466 my_program_dir
= getcwd(0, 0);
4467 ffserver_daemon
= 1;
4469 parse_options(argc
, argv
, options
, NULL
);
4471 unsetenv("http_proxy"); /* Kill the http_proxy */
4473 av_init_random(av_gettime() + (getpid() << 16), &random_state
);
4475 memset(&sigact
, 0, sizeof(sigact
));
4476 sigact
.sa_handler
= handle_child_exit
;
4477 sigact
.sa_flags
= SA_NOCLDSTOP
| SA_RESTART
;
4478 sigaction(SIGCHLD
, &sigact
, 0);
4480 if (parse_ffconfig(config_filename
) < 0) {
4481 fprintf(stderr
, "Incorrect config file - exiting.\n");
4485 /* open log file if needed */
4486 if (logfilename
[0] != '\0') {
4487 if (!strcmp(logfilename
, "-"))
4490 logfile
= fopen(logfilename
, "a");
4491 av_log_set_callback(http_av_log
);
4494 build_file_streams();
4496 build_feed_streams();
4498 compute_bandwidth();
4500 /* put the process in background and detach it from its TTY */
4501 if (ffserver_daemon
) {
4508 } else if (pid
> 0) {
4515 open("/dev/null", O_RDWR
);
4516 if (strcmp(logfilename
, "-") != 0) {
4526 signal(SIGPIPE
, SIG_IGN
);
4528 if (ffserver_daemon
)
4531 if (http_server() < 0) {
4532 http_log("Could not start server\n");