2 * Multiple format streaming server
3 * Copyright (c) 2000, 2001, 2002 Fabrice Bellard
5 * This file is part of FFmpeg.
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 #if HAVE_CLOSESOCKET != 1
24 #define closesocket close
34 #include <sys/ioctl.h>
35 #ifdef HAVE_SYS_POLL_H
40 #undef time //needed because HAVE_AV_CONFIG_H is defined on top
56 /* maximum number of simultaneous HTTP connections */
57 #define HTTP_MAX_CONNECTIONS 2000
60 HTTPSTATE_WAIT_REQUEST
,
61 HTTPSTATE_SEND_HEADER
,
62 HTTPSTATE_SEND_DATA_HEADER
,
63 HTTPSTATE_SEND_DATA
, /* sending TCP or UDP data */
64 HTTPSTATE_SEND_DATA_TRAILER
,
65 HTTPSTATE_RECEIVE_DATA
,
66 HTTPSTATE_WAIT_FEED
, /* wait for data from the feed */
69 RTSPSTATE_WAIT_REQUEST
,
71 RTSPSTATE_SEND_PACKET
,
74 const char *http_state
[] = {
90 #define IOBUFFER_INIT_SIZE 8192
92 /* timeouts are in ms */
93 #define HTTP_REQUEST_TIMEOUT (15 * 1000)
94 #define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
96 #define SYNC_TIMEOUT (10 * 1000)
99 int64_t count1
, count2
;
100 int64_t time1
, time2
;
103 /* context associated with one connection */
104 typedef struct HTTPContext
{
105 enum HTTPState state
;
106 int fd
; /* socket file descriptor */
107 struct sockaddr_in from_addr
; /* origin */
108 struct pollfd
*poll_entry
; /* used when polling */
110 uint8_t *buffer_ptr
, *buffer_end
;
113 struct HTTPContext
*next
;
114 int got_key_frame
; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
118 /* input format handling */
119 AVFormatContext
*fmt_in
;
120 int64_t start_time
; /* In milliseconds - this wraps fairly often */
121 int64_t first_pts
; /* initial pts value */
122 int64_t cur_pts
; /* current pts value from the stream in us */
123 int64_t cur_frame_duration
; /* duration of the current frame in us */
124 int cur_frame_bytes
; /* output frame size, needed to compute
125 the time at which we send each
127 int pts_stream_index
; /* stream we choose as clock reference */
128 int64_t cur_clock
; /* current clock reference value in us */
129 /* output format handling */
130 struct FFStream
*stream
;
131 /* -1 is invalid stream */
132 int feed_streams
[MAX_STREAMS
]; /* index of streams in the feed */
133 int switch_feed_streams
[MAX_STREAMS
]; /* index of streams in the feed */
135 AVFormatContext fmt_ctx
; /* instance of FFStream for one user */
136 int last_packet_sent
; /* true if last data packet was sent */
138 DataRateData datarate
;
145 int is_packetized
; /* if true, the stream is packetized */
146 int packet_stream_index
; /* current stream for output in state machine */
148 /* RTSP state specific */
149 uint8_t *pb_buffer
; /* XXX: use that in all the code */
151 int seq
; /* RTSP sequence number */
153 /* RTP state specific */
154 enum RTSPProtocol rtp_protocol
;
155 char session_id
[32]; /* session id */
156 AVFormatContext
*rtp_ctx
[MAX_STREAMS
];
158 /* RTP/UDP specific */
159 URLContext
*rtp_handles
[MAX_STREAMS
];
161 /* RTP/TCP specific */
162 struct HTTPContext
*rtsp_c
;
163 uint8_t *packet_buffer
, *packet_buffer_ptr
, *packet_buffer_end
;
166 static AVFrame dummy_frame
;
168 /* each generated stream is described here */
172 STREAM_TYPE_REDIRECT
,
175 enum IPAddressAction
{
180 typedef struct IPAddressACL
{
181 struct IPAddressACL
*next
;
182 enum IPAddressAction action
;
183 /* These are in host order */
184 struct in_addr first
;
188 /* description of each stream of the ffserver.conf file */
189 typedef struct FFStream
{
190 enum StreamType stream_type
;
191 char filename
[1024]; /* stream filename */
192 struct FFStream
*feed
; /* feed we are using (can be null if
194 AVFormatParameters
*ap_in
; /* input parameters */
195 AVInputFormat
*ifmt
; /* if non NULL, force input format */
199 int prebuffer
; /* Number of millseconds early to start */
200 int64_t max_time
; /* Number of milliseconds to run */
202 AVStream
*streams
[MAX_STREAMS
];
203 int feed_streams
[MAX_STREAMS
]; /* index of streams in the feed */
204 char feed_filename
[1024]; /* file name of the feed storage, or
205 input file name for a stream */
210 pid_t pid
; /* Of ffmpeg process */
211 time_t pid_start
; /* Of ffmpeg process */
213 struct FFStream
*next
;
214 int bandwidth
; /* bandwidth, in kbits/s */
217 /* multicast specific */
219 struct in_addr multicast_ip
;
220 int multicast_port
; /* first port used for multicast */
222 int loop
; /* if true, send the stream in loops (only meaningful if file) */
225 int feed_opened
; /* true if someone is writing to the feed */
226 int is_feed
; /* true if it is a feed */
227 int readonly
; /* True if writing is prohibited to the file */
229 int64_t bytes_served
;
230 int64_t feed_max_size
; /* maximum storage size, zero means unlimited */
231 int64_t feed_write_index
; /* current write position in feed (it wraps round) */
232 int64_t feed_size
; /* current size of feed */
233 struct FFStream
*next_feed
;
236 typedef struct FeedData
{
237 long long data_count
;
238 float avg_frame_size
; /* frame size averraged over last frames with exponential mean */
241 static struct sockaddr_in my_http_addr
;
242 static struct sockaddr_in my_rtsp_addr
;
244 static char logfilename
[1024];
245 static HTTPContext
*first_http_ctx
;
246 static FFStream
*first_feed
; /* contains only feeds */
247 static FFStream
*first_stream
; /* contains all streams, including feeds */
249 static void new_connection(int server_fd
, int is_rtsp
);
250 static void close_connection(HTTPContext
*c
);
253 static int handle_connection(HTTPContext
*c
);
254 static int http_parse_request(HTTPContext
*c
);
255 static int http_send_data(HTTPContext
*c
);
256 static void compute_stats(HTTPContext
*c
);
257 static int open_input_stream(HTTPContext
*c
, const char *info
);
258 static int http_start_receive_data(HTTPContext
*c
);
259 static int http_receive_data(HTTPContext
*c
);
262 static int rtsp_parse_request(HTTPContext
*c
);
263 static void rtsp_cmd_describe(HTTPContext
*c
, const char *url
);
264 static void rtsp_cmd_options(HTTPContext
*c
, const char *url
);
265 static void rtsp_cmd_setup(HTTPContext
*c
, const char *url
, RTSPHeader
*h
);
266 static void rtsp_cmd_play(HTTPContext
*c
, const char *url
, RTSPHeader
*h
);
267 static void rtsp_cmd_pause(HTTPContext
*c
, const char *url
, RTSPHeader
*h
);
268 static void rtsp_cmd_teardown(HTTPContext
*c
, const char *url
, RTSPHeader
*h
);
271 static int prepare_sdp_description(FFStream
*stream
, uint8_t **pbuffer
,
272 struct in_addr my_ip
);
275 static HTTPContext
*rtp_new_connection(struct sockaddr_in
*from_addr
,
276 FFStream
*stream
, const char *session_id
,
277 enum RTSPProtocol rtp_protocol
);
278 static int rtp_new_av_stream(HTTPContext
*c
,
279 int stream_index
, struct sockaddr_in
*dest_addr
,
280 HTTPContext
*rtsp_c
);
282 static const char *my_program_name
;
283 static const char *my_program_dir
;
285 static int ffserver_debug
;
286 static int ffserver_daemon
;
287 static int no_launch
;
288 static int need_to_start_children
;
290 static int nb_max_connections
;
291 static int nb_connections
;
293 static int max_bandwidth
;
294 static int current_bandwidth
;
296 static int64_t cur_time
; // Making this global saves on passing it around everywhere
298 static AVRandomState random_state
;
300 static FILE *logfile
= NULL
;
302 static void __attribute__ ((format (printf
, 1, 2))) http_log(const char *fmt
, ...)
308 vfprintf(logfile
, fmt
, ap
);
314 static char *ctime1(char *buf2
)
322 p
= buf2
+ strlen(p
) - 1;
328 static void log_connection(HTTPContext
*c
)
335 http_log("%s - - [%s] \"%s %s %s\" %d %"PRId64
"\n",
336 inet_ntoa(c
->from_addr
.sin_addr
),
337 ctime1(buf2
), c
->method
, c
->url
,
338 c
->protocol
, (c
->http_error
? c
->http_error
: 200), c
->data_count
);
341 static void update_datarate(DataRateData
*drd
, int64_t count
)
343 if (!drd
->time1
&& !drd
->count1
) {
344 drd
->time1
= drd
->time2
= cur_time
;
345 drd
->count1
= drd
->count2
= count
;
347 if (cur_time
- drd
->time2
> 5000) {
348 drd
->time1
= drd
->time2
;
349 drd
->count1
= drd
->count2
;
350 drd
->time2
= cur_time
;
356 /* In bytes per second */
357 static int compute_datarate(DataRateData
*drd
, int64_t count
)
359 if (cur_time
== drd
->time1
)
362 return ((count
- drd
->count1
) * 1000) / (cur_time
- drd
->time1
);
366 static void start_children(FFStream
*feed
)
371 for (; feed
; feed
= feed
->next
) {
372 if (feed
->child_argv
&& !feed
->pid
) {
373 feed
->pid_start
= time(0);
378 fprintf(stderr
, "Unable to create children\n");
387 for (i
= 3; i
< 256; i
++) {
391 if (!ffserver_debug
) {
392 i
= open("/dev/null", O_RDWR
);
401 av_strlcpy(pathname
, my_program_name
, sizeof(pathname
));
403 slash
= strrchr(pathname
, '/');
409 strcpy(slash
, "ffmpeg");
411 /* This is needed to make relative pathnames work */
412 chdir(my_program_dir
);
414 signal(SIGPIPE
, SIG_DFL
);
416 execvp(pathname
, feed
->child_argv
);
424 /* open a listening socket */
425 static int socket_open_listen(struct sockaddr_in
*my_addr
)
429 server_fd
= socket(AF_INET
,SOCK_STREAM
,0);
436 setsockopt(server_fd
, SOL_SOCKET
, SO_REUSEADDR
, &tmp
, sizeof(tmp
));
438 if (bind (server_fd
, (struct sockaddr
*) my_addr
, sizeof (*my_addr
)) < 0) {
440 snprintf(bindmsg
, sizeof(bindmsg
), "bind(port %d)", ntohs(my_addr
->sin_port
));
442 closesocket(server_fd
);
446 if (listen (server_fd
, 5) < 0) {
448 closesocket(server_fd
);
451 ff_socket_nonblock(server_fd
, 1);
456 /* start all multicast streams */
457 static void start_multicast(void)
462 struct sockaddr_in dest_addr
;
463 int default_port
, stream_index
;
466 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
467 if (stream
->is_multicast
) {
468 /* open the RTP connection */
469 snprintf(session_id
, sizeof(session_id
), "%08x%08x",
470 av_random(&random_state
), av_random(&random_state
));
472 /* choose a port if none given */
473 if (stream
->multicast_port
== 0) {
474 stream
->multicast_port
= default_port
;
478 dest_addr
.sin_family
= AF_INET
;
479 dest_addr
.sin_addr
= stream
->multicast_ip
;
480 dest_addr
.sin_port
= htons(stream
->multicast_port
);
482 rtp_c
= rtp_new_connection(&dest_addr
, stream
, session_id
,
483 RTSP_PROTOCOL_RTP_UDP_MULTICAST
);
487 if (open_input_stream(rtp_c
, "") < 0) {
488 fprintf(stderr
, "Could not open input stream for stream '%s'\n",
493 /* open each RTP stream */
494 for(stream_index
= 0; stream_index
< stream
->nb_streams
;
496 dest_addr
.sin_port
= htons(stream
->multicast_port
+
498 if (rtp_new_av_stream(rtp_c
, stream_index
, &dest_addr
, NULL
) < 0) {
499 fprintf(stderr
, "Could not open output stream '%s/streamid=%d'\n",
500 stream
->filename
, stream_index
);
505 /* change state to send data */
506 rtp_c
->state
= HTTPSTATE_SEND_DATA
;
511 /* main loop of the http server */
512 static int http_server(void)
514 int server_fd
, ret
, rtsp_server_fd
, delay
, delay1
;
515 struct pollfd poll_table
[HTTP_MAX_CONNECTIONS
+ 2], *poll_entry
;
516 HTTPContext
*c
, *c_next
;
518 server_fd
= socket_open_listen(&my_http_addr
);
522 rtsp_server_fd
= socket_open_listen(&my_rtsp_addr
);
523 if (rtsp_server_fd
< 0)
526 http_log("ffserver started.\n");
528 start_children(first_feed
);
530 first_http_ctx
= NULL
;
536 poll_entry
= poll_table
;
537 poll_entry
->fd
= server_fd
;
538 poll_entry
->events
= POLLIN
;
541 poll_entry
->fd
= rtsp_server_fd
;
542 poll_entry
->events
= POLLIN
;
545 /* wait for events on each HTTP handle */
552 case HTTPSTATE_SEND_HEADER
:
553 case RTSPSTATE_SEND_REPLY
:
554 case RTSPSTATE_SEND_PACKET
:
555 c
->poll_entry
= poll_entry
;
557 poll_entry
->events
= POLLOUT
;
560 case HTTPSTATE_SEND_DATA_HEADER
:
561 case HTTPSTATE_SEND_DATA
:
562 case HTTPSTATE_SEND_DATA_TRAILER
:
563 if (!c
->is_packetized
) {
564 /* for TCP, we output as much as we can (may need to put a limit) */
565 c
->poll_entry
= poll_entry
;
567 poll_entry
->events
= POLLOUT
;
570 /* when ffserver is doing the timing, we work by
571 looking at which packet need to be sent every
573 delay1
= 10; /* one tick wait XXX: 10 ms assumed */
578 case HTTPSTATE_WAIT_REQUEST
:
579 case HTTPSTATE_RECEIVE_DATA
:
580 case HTTPSTATE_WAIT_FEED
:
581 case RTSPSTATE_WAIT_REQUEST
:
582 /* need to catch errors */
583 c
->poll_entry
= poll_entry
;
585 poll_entry
->events
= POLLIN
;/* Maybe this will work */
589 c
->poll_entry
= NULL
;
595 /* wait for an event on one connection. We poll at least every
596 second to handle timeouts */
598 ret
= poll(poll_table
, poll_entry
- poll_table
, delay
);
599 if (ret
< 0 && ff_neterrno() != FF_NETERROR(EAGAIN
) &&
600 ff_neterrno() != FF_NETERROR(EINTR
))
604 cur_time
= av_gettime() / 1000;
606 if (need_to_start_children
) {
607 need_to_start_children
= 0;
608 start_children(first_feed
);
611 /* now handle the events */
612 for(c
= first_http_ctx
; c
!= NULL
; c
= c_next
) {
614 if (handle_connection(c
) < 0) {
615 /* close and free the connection */
621 poll_entry
= poll_table
;
622 /* new HTTP connection request ? */
623 if (poll_entry
->revents
& POLLIN
) {
624 new_connection(server_fd
, 0);
627 /* new RTSP connection request ? */
628 if (poll_entry
->revents
& POLLIN
) {
629 new_connection(rtsp_server_fd
, 1);
634 /* start waiting for a new HTTP/RTSP request */
635 static void start_wait_request(HTTPContext
*c
, int is_rtsp
)
637 c
->buffer_ptr
= c
->buffer
;
638 c
->buffer_end
= c
->buffer
+ c
->buffer_size
- 1; /* leave room for '\0' */
641 c
->timeout
= cur_time
+ RTSP_REQUEST_TIMEOUT
;
642 c
->state
= RTSPSTATE_WAIT_REQUEST
;
644 c
->timeout
= cur_time
+ HTTP_REQUEST_TIMEOUT
;
645 c
->state
= HTTPSTATE_WAIT_REQUEST
;
649 static void new_connection(int server_fd
, int is_rtsp
)
651 struct sockaddr_in from_addr
;
653 HTTPContext
*c
= NULL
;
655 len
= sizeof(from_addr
);
656 fd
= accept(server_fd
, (struct sockaddr
*)&from_addr
,
660 ff_socket_nonblock(fd
, 1);
662 /* XXX: should output a warning page when coming
663 close to the connection limit */
664 if (nb_connections
>= nb_max_connections
)
667 /* add a new connection */
668 c
= av_mallocz(sizeof(HTTPContext
));
673 c
->poll_entry
= NULL
;
674 c
->from_addr
= from_addr
;
675 c
->buffer_size
= IOBUFFER_INIT_SIZE
;
676 c
->buffer
= av_malloc(c
->buffer_size
);
680 c
->next
= first_http_ctx
;
684 start_wait_request(c
, is_rtsp
);
696 static void close_connection(HTTPContext
*c
)
698 HTTPContext
**cp
, *c1
;
700 AVFormatContext
*ctx
;
704 /* remove connection from list */
705 cp
= &first_http_ctx
;
706 while ((*cp
) != NULL
) {
715 /* remove references, if any (XXX: do it faster) */
716 for(c1
= first_http_ctx
; c1
!= NULL
; c1
= c1
->next
) {
721 /* remove connection associated resources */
725 /* close each frame parser */
726 for(i
=0;i
<c
->fmt_in
->nb_streams
;i
++) {
727 st
= c
->fmt_in
->streams
[i
];
728 if (st
->codec
->codec
) {
729 avcodec_close(st
->codec
);
732 av_close_input_file(c
->fmt_in
);
735 /* free RTP output streams if any */
738 nb_streams
= c
->stream
->nb_streams
;
740 for(i
=0;i
<nb_streams
;i
++) {
743 av_write_trailer(ctx
);
746 h
= c
->rtp_handles
[i
];
754 if (!c
->last_packet_sent
) {
757 if (url_open_dyn_buf(&ctx
->pb
) >= 0) {
758 av_write_trailer(ctx
);
759 url_close_dyn_buf(&ctx
->pb
, &c
->pb_buffer
);
764 for(i
=0; i
<ctx
->nb_streams
; i
++)
765 av_free(ctx
->streams
[i
]) ;
767 if (c
->stream
&& !c
->post
&& c
->stream
->stream_type
== STREAM_TYPE_LIVE
)
768 current_bandwidth
-= c
->stream
->bandwidth
;
770 /* signal that there is no feed if we are the feeder socket */
771 if (c
->state
== HTTPSTATE_RECEIVE_DATA
&& c
->stream
) {
772 c
->stream
->feed_opened
= 0;
776 av_freep(&c
->pb_buffer
);
777 av_freep(&c
->packet_buffer
);
783 static int handle_connection(HTTPContext
*c
)
788 case HTTPSTATE_WAIT_REQUEST
:
789 case RTSPSTATE_WAIT_REQUEST
:
791 if ((c
->timeout
- cur_time
) < 0)
793 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
))
796 /* no need to read if no events */
797 if (!(c
->poll_entry
->revents
& POLLIN
))
801 len
= recv(c
->fd
, c
->buffer_ptr
, 1, 0);
803 if (ff_neterrno() != FF_NETERROR(EAGAIN
) &&
804 ff_neterrno() != FF_NETERROR(EINTR
))
806 } else if (len
== 0) {
809 /* search for end of request. */
811 c
->buffer_ptr
+= len
;
813 if ((ptr
>= c
->buffer
+ 2 && !memcmp(ptr
-2, "\n\n", 2)) ||
814 (ptr
>= c
->buffer
+ 4 && !memcmp(ptr
-4, "\r\n\r\n", 4))) {
815 /* request found : parse it and reply */
816 if (c
->state
== HTTPSTATE_WAIT_REQUEST
) {
817 ret
= http_parse_request(c
);
819 ret
= rtsp_parse_request(c
);
823 } else if (ptr
>= c
->buffer_end
) {
824 /* request too long: cannot do anything */
826 } else goto read_loop
;
830 case HTTPSTATE_SEND_HEADER
:
831 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
))
834 /* no need to write if no events */
835 if (!(c
->poll_entry
->revents
& POLLOUT
))
837 len
= send(c
->fd
, c
->buffer_ptr
, c
->buffer_end
- c
->buffer_ptr
, 0);
839 if (ff_neterrno() != FF_NETERROR(EAGAIN
) &&
840 ff_neterrno() != FF_NETERROR(EINTR
)) {
841 /* error : close connection */
842 av_freep(&c
->pb_buffer
);
846 c
->buffer_ptr
+= len
;
848 c
->stream
->bytes_served
+= len
;
849 c
->data_count
+= len
;
850 if (c
->buffer_ptr
>= c
->buffer_end
) {
851 av_freep(&c
->pb_buffer
);
856 /* all the buffer was sent : synchronize to the incoming stream */
857 c
->state
= HTTPSTATE_SEND_DATA_HEADER
;
858 c
->buffer_ptr
= c
->buffer_end
= c
->buffer
;
863 case HTTPSTATE_SEND_DATA
:
864 case HTTPSTATE_SEND_DATA_HEADER
:
865 case HTTPSTATE_SEND_DATA_TRAILER
:
866 /* for packetized output, we consider we can always write (the
867 input streams sets the speed). It may be better to verify
868 that we do not rely too much on the kernel queues */
869 if (!c
->is_packetized
) {
870 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
))
873 /* no need to read if no events */
874 if (!(c
->poll_entry
->revents
& POLLOUT
))
877 if (http_send_data(c
) < 0)
879 /* close connection if trailer sent */
880 if (c
->state
== HTTPSTATE_SEND_DATA_TRAILER
)
883 case HTTPSTATE_RECEIVE_DATA
:
884 /* no need to read if no events */
885 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
))
887 if (!(c
->poll_entry
->revents
& POLLIN
))
889 if (http_receive_data(c
) < 0)
892 case HTTPSTATE_WAIT_FEED
:
893 /* no need to read if no events */
894 if (c
->poll_entry
->revents
& (POLLIN
| POLLERR
| POLLHUP
))
897 /* nothing to do, we'll be waken up by incoming feed packets */
900 case RTSPSTATE_SEND_REPLY
:
901 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
)) {
902 av_freep(&c
->pb_buffer
);
905 /* no need to write if no events */
906 if (!(c
->poll_entry
->revents
& POLLOUT
))
908 len
= send(c
->fd
, c
->buffer_ptr
, c
->buffer_end
- c
->buffer_ptr
, 0);
910 if (ff_neterrno() != FF_NETERROR(EAGAIN
) &&
911 ff_neterrno() != FF_NETERROR(EINTR
)) {
912 /* error : close connection */
913 av_freep(&c
->pb_buffer
);
917 c
->buffer_ptr
+= len
;
918 c
->data_count
+= len
;
919 if (c
->buffer_ptr
>= c
->buffer_end
) {
920 /* all the buffer was sent : wait for a new request */
921 av_freep(&c
->pb_buffer
);
922 start_wait_request(c
, 1);
926 case RTSPSTATE_SEND_PACKET
:
927 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
)) {
928 av_freep(&c
->packet_buffer
);
931 /* no need to write if no events */
932 if (!(c
->poll_entry
->revents
& POLLOUT
))
934 len
= send(c
->fd
, c
->packet_buffer_ptr
,
935 c
->packet_buffer_end
- c
->packet_buffer_ptr
, 0);
937 if (ff_neterrno() != FF_NETERROR(EAGAIN
) &&
938 ff_neterrno() != FF_NETERROR(EINTR
)) {
939 /* error : close connection */
940 av_freep(&c
->packet_buffer
);
944 c
->packet_buffer_ptr
+= len
;
945 if (c
->packet_buffer_ptr
>= c
->packet_buffer_end
) {
946 /* all the buffer was sent : wait for a new request */
947 av_freep(&c
->packet_buffer
);
948 c
->state
= RTSPSTATE_WAIT_REQUEST
;
952 case HTTPSTATE_READY
:
961 static int extract_rates(char *rates
, int ratelen
, const char *request
)
965 for (p
= request
; *p
&& *p
!= '\r' && *p
!= '\n'; ) {
966 if (strncasecmp(p
, "Pragma:", 7) == 0) {
967 const char *q
= p
+ 7;
969 while (*q
&& *q
!= '\n' && isspace(*q
))
972 if (strncasecmp(q
, "stream-switch-entry=", 20) == 0) {
978 memset(rates
, 0xff, ratelen
);
981 while (*q
&& *q
!= '\n' && *q
!= ':')
984 if (sscanf(q
, ":%d:%d", &stream_no
, &rate_no
) != 2) {
988 if (stream_no
< ratelen
&& stream_no
>= 0) {
989 rates
[stream_no
] = rate_no
;
992 while (*q
&& *q
!= '\n' && !isspace(*q
))
1009 static int find_stream_in_feed(FFStream
*feed
, AVCodecContext
*codec
, int bit_rate
)
1012 int best_bitrate
= 100000000;
1015 for (i
= 0; i
< feed
->nb_streams
; i
++) {
1016 AVCodecContext
*feed_codec
= feed
->streams
[i
]->codec
;
1018 if (feed_codec
->codec_id
!= codec
->codec_id
||
1019 feed_codec
->sample_rate
!= codec
->sample_rate
||
1020 feed_codec
->width
!= codec
->width
||
1021 feed_codec
->height
!= codec
->height
) {
1025 /* Potential stream */
1027 /* We want the fastest stream less than bit_rate, or the slowest
1028 * faster than bit_rate
1031 if (feed_codec
->bit_rate
<= bit_rate
) {
1032 if (best_bitrate
> bit_rate
|| feed_codec
->bit_rate
> best_bitrate
) {
1033 best_bitrate
= feed_codec
->bit_rate
;
1037 if (feed_codec
->bit_rate
< best_bitrate
) {
1038 best_bitrate
= feed_codec
->bit_rate
;
1047 static int modify_current_stream(HTTPContext
*c
, char *rates
)
1050 FFStream
*req
= c
->stream
;
1051 int action_required
= 0;
1053 /* Not much we can do for a feed */
1057 for (i
= 0; i
< req
->nb_streams
; i
++) {
1058 AVCodecContext
*codec
= req
->streams
[i
]->codec
;
1062 c
->switch_feed_streams
[i
] = req
->feed_streams
[i
];
1065 c
->switch_feed_streams
[i
] = find_stream_in_feed(req
->feed
, codec
, codec
->bit_rate
/ 2);
1068 /* Wants off or slow */
1069 c
->switch_feed_streams
[i
] = find_stream_in_feed(req
->feed
, codec
, codec
->bit_rate
/ 4);
1071 /* This doesn't work well when it turns off the only stream! */
1072 c
->switch_feed_streams
[i
] = -2;
1073 c
->feed_streams
[i
] = -2;
1078 if (c
->switch_feed_streams
[i
] >= 0 && c
->switch_feed_streams
[i
] != c
->feed_streams
[i
])
1079 action_required
= 1;
1082 return action_required
;
1086 static void do_switch_stream(HTTPContext
*c
, int i
)
1088 if (c
->switch_feed_streams
[i
] >= 0) {
1090 c
->feed_streams
[i
] = c
->switch_feed_streams
[i
];
1093 /* Now update the stream */
1095 c
->switch_feed_streams
[i
] = -1;
1098 /* XXX: factorize in utils.c ? */
1099 /* XXX: take care with different space meaning */
1100 static void skip_spaces(const char **pp
)
1104 while (*p
== ' ' || *p
== '\t')
1109 static void get_word(char *buf
, int buf_size
, const char **pp
)
1117 while (!isspace(*p
) && *p
!= '\0') {
1118 if ((q
- buf
) < buf_size
- 1)
1127 static int validate_acl(FFStream
*stream
, HTTPContext
*c
)
1129 enum IPAddressAction last_action
= IP_DENY
;
1131 struct in_addr
*src
= &c
->from_addr
.sin_addr
;
1132 unsigned long src_addr
= src
->s_addr
;
1134 for (acl
= stream
->acl
; acl
; acl
= acl
->next
) {
1135 if (src_addr
>= acl
->first
.s_addr
&& src_addr
<= acl
->last
.s_addr
) {
1136 return (acl
->action
== IP_ALLOW
) ? 1 : 0;
1138 last_action
= acl
->action
;
1141 /* Nothing matched, so return not the last action */
1142 return (last_action
== IP_DENY
) ? 1 : 0;
1145 /* compute the real filename of a file by matching it without its
1146 extensions to all the stream filenames */
1147 static void compute_real_filename(char *filename
, int max_size
)
1154 /* compute filename by matching without the file extensions */
1155 av_strlcpy(file1
, filename
, sizeof(file1
));
1156 p
= strrchr(file1
, '.');
1159 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
1160 av_strlcpy(file2
, stream
->filename
, sizeof(file2
));
1161 p
= strrchr(file2
, '.');
1164 if (!strcmp(file1
, file2
)) {
1165 av_strlcpy(filename
, stream
->filename
, max_size
);
1180 /* parse http request and prepare header */
1181 static int http_parse_request(HTTPContext
*c
)
1184 enum RedirType redir_type
;
1186 char info
[1024], filename
[1024];
1190 const char *mime_type
;
1194 char *useragent
= 0;
1197 get_word(cmd
, sizeof(cmd
), (const char **)&p
);
1198 av_strlcpy(c
->method
, cmd
, sizeof(c
->method
));
1200 if (!strcmp(cmd
, "GET"))
1202 else if (!strcmp(cmd
, "POST"))
1207 get_word(url
, sizeof(url
), (const char **)&p
);
1208 av_strlcpy(c
->url
, url
, sizeof(c
->url
));
1210 get_word(protocol
, sizeof(protocol
), (const char **)&p
);
1211 if (strcmp(protocol
, "HTTP/1.0") && strcmp(protocol
, "HTTP/1.1"))
1214 av_strlcpy(c
->protocol
, protocol
, sizeof(c
->protocol
));
1217 http_log("New connection: %s %s\n", cmd
, url
);
1219 /* find the filename and the optional info string in the request */
1220 p
= strchr(url
, '?');
1222 av_strlcpy(info
, p
, sizeof(info
));
1228 av_strlcpy(filename
, url
+ ((*url
== '/') ? 1 : 0), sizeof(filename
)-1);
1230 for (p
= c
->buffer
; *p
&& *p
!= '\r' && *p
!= '\n'; ) {
1231 if (strncasecmp(p
, "User-Agent:", 11) == 0) {
1233 if (*useragent
&& *useragent
!= '\n' && isspace(*useragent
))
1237 p
= strchr(p
, '\n');
1244 redir_type
= REDIR_NONE
;
1245 if (match_ext(filename
, "asx")) {
1246 redir_type
= REDIR_ASX
;
1247 filename
[strlen(filename
)-1] = 'f';
1248 } else if (match_ext(filename
, "asf") &&
1249 (!useragent
|| strncasecmp(useragent
, "NSPlayer", 8) != 0)) {
1250 /* if this isn't WMP or lookalike, return the redirector file */
1251 redir_type
= REDIR_ASF
;
1252 } else if (match_ext(filename
, "rpm,ram")) {
1253 redir_type
= REDIR_RAM
;
1254 strcpy(filename
+ strlen(filename
)-2, "m");
1255 } else if (match_ext(filename
, "rtsp")) {
1256 redir_type
= REDIR_RTSP
;
1257 compute_real_filename(filename
, sizeof(filename
) - 1);
1258 } else if (match_ext(filename
, "sdp")) {
1259 redir_type
= REDIR_SDP
;
1260 compute_real_filename(filename
, sizeof(filename
) - 1);
1263 // "redirect" / request to index.html
1264 if (!strlen(filename
))
1265 av_strlcpy(filename
, "index.html", sizeof(filename
) - 1);
1267 stream
= first_stream
;
1268 while (stream
!= NULL
) {
1269 if (!strcmp(stream
->filename
, filename
) && validate_acl(stream
, c
))
1271 stream
= stream
->next
;
1273 if (stream
== NULL
) {
1274 snprintf(msg
, sizeof(msg
), "File '%s' not found", url
);
1279 memcpy(c
->feed_streams
, stream
->feed_streams
, sizeof(c
->feed_streams
));
1280 memset(c
->switch_feed_streams
, -1, sizeof(c
->switch_feed_streams
));
1282 if (stream
->stream_type
== STREAM_TYPE_REDIRECT
) {
1283 c
->http_error
= 301;
1285 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "HTTP/1.0 301 Moved\r\n");
1286 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "Location: %s\r\n", stream
->feed_filename
);
1287 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "Content-type: text/html\r\n");
1288 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "\r\n");
1289 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "<html><head><title>Moved</title></head><body>\r\n");
1290 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "You should be <a href=\"%s\">redirected</a>.\r\n", stream
->feed_filename
);
1291 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "</body></html>\r\n");
1293 /* prepare output buffer */
1294 c
->buffer_ptr
= c
->buffer
;
1296 c
->state
= HTTPSTATE_SEND_HEADER
;
1300 /* If this is WMP, get the rate information */
1301 if (extract_rates(ratebuf
, sizeof(ratebuf
), c
->buffer
)) {
1302 if (modify_current_stream(c
, ratebuf
)) {
1303 for (i
= 0; i
< sizeof(c
->feed_streams
) / sizeof(c
->feed_streams
[0]); i
++) {
1304 if (c
->switch_feed_streams
[i
] >= 0)
1305 do_switch_stream(c
, i
);
1310 /* If already streaming this feed, do not let start another feeder. */
1311 if (stream
->feed_opened
) {
1312 snprintf(msg
, sizeof(msg
), "This feed is already being received.");
1316 if (c
->post
== 0 && stream
->stream_type
== STREAM_TYPE_LIVE
) {
1317 current_bandwidth
+= stream
->bandwidth
;
1320 if (c
->post
== 0 && max_bandwidth
< current_bandwidth
) {
1321 c
->http_error
= 200;
1323 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "HTTP/1.0 200 Server too busy\r\n");
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>Too busy</title></head><body>\r\n");
1327 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");
1328 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "<p>The bandwidth being served (including your stream) is %dkbit/sec, and this exceeds the limit of %dkbit/sec.</p>\r\n",
1329 current_bandwidth
, max_bandwidth
);
1330 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "</body></html>\r\n");
1332 /* prepare output buffer */
1333 c
->buffer_ptr
= c
->buffer
;
1335 c
->state
= HTTPSTATE_SEND_HEADER
;
1339 if (redir_type
!= REDIR_NONE
) {
1342 for (p
= c
->buffer
; *p
&& *p
!= '\r' && *p
!= '\n'; ) {
1343 if (strncasecmp(p
, "Host:", 5) == 0) {
1347 p
= strchr(p
, '\n');
1358 while (isspace(*hostinfo
))
1361 eoh
= strchr(hostinfo
, '\n');
1363 if (eoh
[-1] == '\r')
1366 if (eoh
- hostinfo
< sizeof(hostbuf
) - 1) {
1367 memcpy(hostbuf
, hostinfo
, eoh
- hostinfo
);
1368 hostbuf
[eoh
- hostinfo
] = 0;
1370 c
->http_error
= 200;
1372 switch(redir_type
) {
1374 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "HTTP/1.0 200 ASX Follows\r\n");
1375 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "Content-type: video/x-ms-asf\r\n");
1376 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "\r\n");
1377 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "<ASX Version=\"3\">\r\n");
1378 //q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<!-- Autogenerated by ffserver -->\r\n");
1379 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n",
1380 hostbuf
, filename
, info
);
1381 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "</ASX>\r\n");
1384 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "HTTP/1.0 200 RAM Follows\r\n");
1385 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "Content-type: audio/x-pn-realaudio\r\n");
1386 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "\r\n");
1387 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "# Autogenerated by ffserver\r\n");
1388 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "http://%s/%s%s\r\n",
1389 hostbuf
, filename
, info
);
1392 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "HTTP/1.0 200 ASF Redirect follows\r\n");
1393 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "Content-type: video/x-ms-asf\r\n");
1394 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "\r\n");
1395 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "[Reference]\r\n");
1396 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "Ref1=http://%s/%s%s\r\n",
1397 hostbuf
, filename
, info
);
1401 char hostname
[256], *p
;
1402 /* extract only hostname */
1403 av_strlcpy(hostname
, hostbuf
, sizeof(hostname
));
1404 p
= strrchr(hostname
, ':');
1407 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "HTTP/1.0 200 RTSP Redirect follows\r\n");
1408 /* XXX: incorrect mime type ? */
1409 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "Content-type: application/x-rtsp\r\n");
1410 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "\r\n");
1411 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "rtsp://%s:%d/%s\r\n",
1412 hostname
, ntohs(my_rtsp_addr
.sin_port
),
1419 int sdp_data_size
, len
;
1420 struct sockaddr_in my_addr
;
1422 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "HTTP/1.0 200 OK\r\n");
1423 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "Content-type: application/sdp\r\n");
1424 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "\r\n");
1426 len
= sizeof(my_addr
);
1427 getsockname(c
->fd
, (struct sockaddr
*)&my_addr
, &len
);
1429 /* XXX: should use a dynamic buffer */
1430 sdp_data_size
= prepare_sdp_description(stream
,
1433 if (sdp_data_size
> 0) {
1434 memcpy(q
, sdp_data
, sdp_data_size
);
1446 /* prepare output buffer */
1447 c
->buffer_ptr
= c
->buffer
;
1449 c
->state
= HTTPSTATE_SEND_HEADER
;
1455 snprintf(msg
, sizeof(msg
), "ASX/RAM file not handled");
1459 stream
->conns_served
++;
1461 /* XXX: add there authenticate and IP match */
1464 /* if post, it means a feed is being sent */
1465 if (!stream
->is_feed
) {
1466 /* However it might be a status report from WMP! Lets log the data
1467 * as it might come in handy one day
1472 for (p
= c
->buffer
; *p
&& *p
!= '\r' && *p
!= '\n'; ) {
1473 if (strncasecmp(p
, "Pragma: log-line=", 17) == 0) {
1477 if (strncasecmp(p
, "Pragma: client-id=", 18) == 0) {
1478 client_id
= strtol(p
+ 18, 0, 10);
1480 p
= strchr(p
, '\n');
1488 char *eol
= strchr(logline
, '\n');
1493 if (eol
[-1] == '\r')
1495 http_log("%.*s\n", (int) (eol
- logline
), logline
);
1496 c
->suppress_log
= 1;
1501 http_log("\nGot request:\n%s\n", c
->buffer
);
1504 if (client_id
&& extract_rates(ratebuf
, sizeof(ratebuf
), c
->buffer
)) {
1507 /* Now we have to find the client_id */
1508 for (wmpc
= first_http_ctx
; wmpc
; wmpc
= wmpc
->next
) {
1509 if (wmpc
->wmp_client_id
== client_id
)
1514 if (modify_current_stream(wmpc
, ratebuf
)) {
1515 wmpc
->switch_pending
= 1;
1520 snprintf(msg
, sizeof(msg
), "POST command not handled");
1524 if (http_start_receive_data(c
) < 0) {
1525 snprintf(msg
, sizeof(msg
), "could not open feed");
1529 c
->state
= HTTPSTATE_RECEIVE_DATA
;
1534 if (strcmp(stream
->filename
+ strlen(stream
->filename
) - 4, ".asf") == 0) {
1535 http_log("\nGot request:\n%s\n", c
->buffer
);
1539 if (c
->stream
->stream_type
== STREAM_TYPE_STATUS
)
1542 /* open input stream */
1543 if (open_input_stream(c
, info
) < 0) {
1544 snprintf(msg
, sizeof(msg
), "Input stream corresponding to '%s' not found", url
);
1548 /* prepare http header */
1550 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "HTTP/1.0 200 OK\r\n");
1551 mime_type
= c
->stream
->fmt
->mime_type
;
1553 mime_type
= "application/x-octet-stream";
1554 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "Pragma: no-cache\r\n");
1556 /* for asf, we need extra headers */
1557 if (!strcmp(c
->stream
->fmt
->name
,"asf_stream")) {
1558 /* Need to allocate a client id */
1560 c
->wmp_client_id
= av_random(&random_state
) & 0x7fffffff;
1562 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
);
1564 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "Content-Type: %s\r\n", mime_type
);
1565 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "\r\n");
1567 /* prepare output buffer */
1569 c
->buffer_ptr
= c
->buffer
;
1571 c
->state
= HTTPSTATE_SEND_HEADER
;
1574 c
->http_error
= 404;
1576 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "HTTP/1.0 404 Not Found\r\n");
1577 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "Content-type: %s\r\n", "text/html");
1578 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "\r\n");
1579 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "<HTML>\n");
1580 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n");
1581 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "<BODY>%s</BODY>\n", msg
);
1582 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "</HTML>\n");
1584 /* prepare output buffer */
1585 c
->buffer_ptr
= c
->buffer
;
1587 c
->state
= HTTPSTATE_SEND_HEADER
;
1591 c
->http_error
= 200; /* horrible : we use this value to avoid
1592 going to the send data state */
1593 c
->state
= HTTPSTATE_SEND_HEADER
;
1597 static void fmt_bytecount(ByteIOContext
*pb
, int64_t count
)
1599 static const char *suffix
= " kMGTP";
1602 for (s
= suffix
; count
>= 100000 && s
[1]; count
/= 1000, s
++) {
1605 url_fprintf(pb
, "%"PRId64
"%c", count
, *s
);
1608 static void compute_stats(HTTPContext
*c
)
1615 ByteIOContext pb1
, *pb
= &pb1
;
1617 if (url_open_dyn_buf(pb
) < 0) {
1618 /* XXX: return an error ? */
1619 c
->buffer_ptr
= c
->buffer
;
1620 c
->buffer_end
= c
->buffer
;
1624 url_fprintf(pb
, "HTTP/1.0 200 OK\r\n");
1625 url_fprintf(pb
, "Content-type: %s\r\n", "text/html");
1626 url_fprintf(pb
, "Pragma: no-cache\r\n");
1627 url_fprintf(pb
, "\r\n");
1629 url_fprintf(pb
, "<HEAD><TITLE>FFServer Status</TITLE>\n");
1630 if (c
->stream
->feed_filename
) {
1631 url_fprintf(pb
, "<link rel=\"shortcut icon\" href=\"%s\">\n", c
->stream
->feed_filename
);
1633 url_fprintf(pb
, "</HEAD>\n<BODY>");
1634 url_fprintf(pb
, "<H1>FFServer Status</H1>\n");
1636 url_fprintf(pb
, "<H2>Available Streams</H2>\n");
1637 url_fprintf(pb
, "<TABLE cellspacing=0 cellpadding=4>\n");
1638 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");
1639 stream
= first_stream
;
1640 while (stream
!= NULL
) {
1641 char sfilename
[1024];
1644 if (stream
->feed
!= stream
) {
1645 av_strlcpy(sfilename
, stream
->filename
, sizeof(sfilename
) - 10);
1646 eosf
= sfilename
+ strlen(sfilename
);
1647 if (eosf
- sfilename
>= 4) {
1648 if (strcmp(eosf
- 4, ".asf") == 0) {
1649 strcpy(eosf
- 4, ".asx");
1650 } else if (strcmp(eosf
- 3, ".rm") == 0) {
1651 strcpy(eosf
- 3, ".ram");
1652 } else if (stream
->fmt
== &rtp_muxer
) {
1653 /* generate a sample RTSP director if
1654 unicast. Generate an SDP redirector if
1656 eosf
= strrchr(sfilename
, '.');
1658 eosf
= sfilename
+ strlen(sfilename
);
1659 if (stream
->is_multicast
)
1660 strcpy(eosf
, ".sdp");
1662 strcpy(eosf
, ".rtsp");
1666 url_fprintf(pb
, "<TR><TD><A HREF=\"/%s\">%s</A> ",
1667 sfilename
, stream
->filename
);
1668 url_fprintf(pb
, "<td align=right> %d <td align=right> ",
1669 stream
->conns_served
);
1670 fmt_bytecount(pb
, stream
->bytes_served
);
1671 switch(stream
->stream_type
) {
1672 case STREAM_TYPE_LIVE
:
1674 int audio_bit_rate
= 0;
1675 int video_bit_rate
= 0;
1676 const char *audio_codec_name
= "";
1677 const char *video_codec_name
= "";
1678 const char *audio_codec_name_extra
= "";
1679 const char *video_codec_name_extra
= "";
1681 for(i
=0;i
<stream
->nb_streams
;i
++) {
1682 AVStream
*st
= stream
->streams
[i
];
1683 AVCodec
*codec
= avcodec_find_encoder(st
->codec
->codec_id
);
1684 switch(st
->codec
->codec_type
) {
1685 case CODEC_TYPE_AUDIO
:
1686 audio_bit_rate
+= st
->codec
->bit_rate
;
1688 if (*audio_codec_name
)
1689 audio_codec_name_extra
= "...";
1690 audio_codec_name
= codec
->name
;
1693 case CODEC_TYPE_VIDEO
:
1694 video_bit_rate
+= st
->codec
->bit_rate
;
1696 if (*video_codec_name
)
1697 video_codec_name_extra
= "...";
1698 video_codec_name
= codec
->name
;
1701 case CODEC_TYPE_DATA
:
1702 video_bit_rate
+= st
->codec
->bit_rate
;
1708 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",
1711 video_bit_rate
/ 1000, video_codec_name
, video_codec_name_extra
,
1712 audio_bit_rate
/ 1000, audio_codec_name
, audio_codec_name_extra
);
1714 url_fprintf(pb
, "<TD>%s", stream
->feed
->filename
);
1716 url_fprintf(pb
, "<TD>%s", stream
->feed_filename
);
1718 url_fprintf(pb
, "\n");
1722 url_fprintf(pb
, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
1726 stream
= stream
->next
;
1728 url_fprintf(pb
, "</TABLE>\n");
1730 stream
= first_stream
;
1731 while (stream
!= NULL
) {
1732 if (stream
->feed
== stream
) {
1733 url_fprintf(pb
, "<h2>Feed %s</h2>", stream
->filename
);
1735 url_fprintf(pb
, "Running as pid %d.\n", stream
->pid
);
1737 #if defined(linux) && !defined(CONFIG_NOCUTILS)
1742 /* This is somewhat linux specific I guess */
1743 snprintf(ps_cmd
, sizeof(ps_cmd
),
1744 "ps -o \"%%cpu,cputime\" --no-headers %d",
1747 pid_stat
= popen(ps_cmd
, "r");
1752 if (fscanf(pid_stat
, "%10s %64s", cpuperc
,
1754 url_fprintf(pb
, "Currently using %s%% of the cpu. Total time used %s.\n",
1762 url_fprintf(pb
, "<p>");
1764 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");
1766 for (i
= 0; i
< stream
->nb_streams
; i
++) {
1767 AVStream
*st
= stream
->streams
[i
];
1768 AVCodec
*codec
= avcodec_find_encoder(st
->codec
->codec_id
);
1769 const char *type
= "unknown";
1770 char parameters
[64];
1774 switch(st
->codec
->codec_type
) {
1775 case CODEC_TYPE_AUDIO
:
1777 snprintf(parameters
, sizeof(parameters
), "%d channel(s), %d Hz", st
->codec
->channels
, st
->codec
->sample_rate
);
1779 case CODEC_TYPE_VIDEO
:
1781 snprintf(parameters
, sizeof(parameters
), "%dx%d, q=%d-%d, fps=%d", st
->codec
->width
, st
->codec
->height
,
1782 st
->codec
->qmin
, st
->codec
->qmax
, st
->codec
->time_base
.den
/ st
->codec
->time_base
.num
);
1787 url_fprintf(pb
, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
1788 i
, type
, st
->codec
->bit_rate
/1000, codec
? codec
->name
: "", parameters
);
1790 url_fprintf(pb
, "</table>\n");
1793 stream
= stream
->next
;
1799 AVCodecContext
*enc
;
1803 stream
= first_feed
;
1804 while (stream
!= NULL
) {
1805 url_fprintf(pb
, "<H1>Feed '%s'</H1>\n", stream
->filename
);
1806 url_fprintf(pb
, "<TABLE>\n");
1807 url_fprintf(pb
, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n");
1808 for(i
=0;i
<stream
->nb_streams
;i
++) {
1809 AVStream
*st
= stream
->streams
[i
];
1810 FeedData
*fdata
= st
->priv_data
;
1813 avcodec_string(buf
, sizeof(buf
), enc
);
1814 avg
= fdata
->avg_frame_size
* (float)enc
->rate
* 8.0;
1815 if (enc
->codec
->type
== CODEC_TYPE_AUDIO
&& enc
->frame_size
> 0)
1816 avg
/= enc
->frame_size
;
1817 url_fprintf(pb
, "<TR><TD>%s <TD> %d <TD> %"PRId64
" <TD> %0.1f\n",
1818 buf
, enc
->frame_number
, fdata
->data_count
, avg
/ 1000.0);
1820 url_fprintf(pb
, "</TABLE>\n");
1821 stream
= stream
->next_feed
;
1826 /* connection status */
1827 url_fprintf(pb
, "<H2>Connection Status</H2>\n");
1829 url_fprintf(pb
, "Number of connections: %d / %d<BR>\n",
1830 nb_connections
, nb_max_connections
);
1832 url_fprintf(pb
, "Bandwidth in use: %dk / %dk<BR>\n",
1833 current_bandwidth
, max_bandwidth
);
1835 url_fprintf(pb
, "<TABLE>\n");
1836 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");
1837 c1
= first_http_ctx
;
1839 while (c1
!= NULL
) {
1845 for (j
= 0; j
< c1
->stream
->nb_streams
; j
++) {
1846 if (!c1
->stream
->feed
) {
1847 bitrate
+= c1
->stream
->streams
[j
]->codec
->bit_rate
;
1849 if (c1
->feed_streams
[j
] >= 0) {
1850 bitrate
+= c1
->stream
->feed
->streams
[c1
->feed_streams
[j
]]->codec
->bit_rate
;
1857 p
= inet_ntoa(c1
->from_addr
.sin_addr
);
1858 url_fprintf(pb
, "<TR><TD><B>%d</B><TD>%s%s<TD>%s<TD>%s<TD>%s<td align=right>",
1860 c1
->stream
? c1
->stream
->filename
: "",
1861 c1
->state
== HTTPSTATE_RECEIVE_DATA
? "(input)" : "",
1864 http_state
[c1
->state
]);
1865 fmt_bytecount(pb
, bitrate
);
1866 url_fprintf(pb
, "<td align=right>");
1867 fmt_bytecount(pb
, compute_datarate(&c1
->datarate
, c1
->data_count
) * 8);
1868 url_fprintf(pb
, "<td align=right>");
1869 fmt_bytecount(pb
, c1
->data_count
);
1870 url_fprintf(pb
, "\n");
1873 url_fprintf(pb
, "</TABLE>\n");
1878 url_fprintf(pb
, "<HR size=1 noshade>Generated at %s", p
);
1879 url_fprintf(pb
, "</BODY>\n</HTML>\n");
1881 len
= url_close_dyn_buf(pb
, &c
->pb_buffer
);
1882 c
->buffer_ptr
= c
->pb_buffer
;
1883 c
->buffer_end
= c
->pb_buffer
+ len
;
1886 /* check if the parser needs to be opened for stream i */
1887 static void open_parser(AVFormatContext
*s
, int i
)
1889 AVStream
*st
= s
->streams
[i
];
1892 if (!st
->codec
->codec
) {
1893 codec
= avcodec_find_decoder(st
->codec
->codec_id
);
1894 if (codec
&& (codec
->capabilities
& CODEC_CAP_PARSE_ONLY
)) {
1895 st
->codec
->parse_only
= 1;
1896 if (avcodec_open(st
->codec
, codec
) < 0) {
1897 st
->codec
->parse_only
= 0;
1903 static int open_input_stream(HTTPContext
*c
, const char *info
)
1906 char input_filename
[1024];
1911 /* find file name */
1912 if (c
->stream
->feed
) {
1913 strcpy(input_filename
, c
->stream
->feed
->feed_filename
);
1914 buf_size
= FFM_PACKET_SIZE
;
1915 /* compute position (absolute time) */
1916 if (find_info_tag(buf
, sizeof(buf
), "date", info
)) {
1917 stream_pos
= parse_date(buf
, 0);
1918 } else if (find_info_tag(buf
, sizeof(buf
), "buffer", info
)) {
1919 int prebuffer
= strtol(buf
, 0, 10);
1920 stream_pos
= av_gettime() - prebuffer
* (int64_t)1000000;
1922 stream_pos
= av_gettime() - c
->stream
->prebuffer
* (int64_t)1000;
1925 strcpy(input_filename
, c
->stream
->feed_filename
);
1927 /* compute position (relative time) */
1928 if (find_info_tag(buf
, sizeof(buf
), "date", info
)) {
1929 stream_pos
= parse_date(buf
, 1);
1934 if (input_filename
[0] == '\0')
1938 { time_t when
= stream_pos
/ 1000000;
1939 http_log("Stream pos = %"PRId64
", time=%s", stream_pos
, ctime(&when
));
1944 if (av_open_input_file(&s
, input_filename
, c
->stream
->ifmt
,
1945 buf_size
, c
->stream
->ap_in
) < 0) {
1946 http_log("%s not found", input_filename
);
1951 /* open each parser */
1952 for(i
=0;i
<s
->nb_streams
;i
++)
1955 /* choose stream as clock source (we favorize video stream if
1956 present) for packet sending */
1957 c
->pts_stream_index
= 0;
1958 for(i
=0;i
<c
->stream
->nb_streams
;i
++) {
1959 if (c
->pts_stream_index
== 0 &&
1960 c
->stream
->streams
[i
]->codec
->codec_type
== CODEC_TYPE_VIDEO
) {
1961 c
->pts_stream_index
= i
;
1966 if (c
->fmt_in
->iformat
->read_seek
) {
1967 c
->fmt_in
->iformat
->read_seek(c
->fmt_in
, 0, stream_pos
, 0);
1970 /* set the start time (needed for maxtime and RTP packet timing) */
1971 c
->start_time
= cur_time
;
1972 c
->first_pts
= AV_NOPTS_VALUE
;
1976 /* return the server clock (in us) */
1977 static int64_t get_server_clock(HTTPContext
*c
)
1979 /* compute current pts value from system time */
1980 return (cur_time
- c
->start_time
) * 1000;
1983 /* return the estimated time at which the current packet must be sent
1985 static int64_t get_packet_send_clock(HTTPContext
*c
)
1987 int bytes_left
, bytes_sent
, frame_bytes
;
1989 frame_bytes
= c
->cur_frame_bytes
;
1990 if (frame_bytes
<= 0) {
1993 bytes_left
= c
->buffer_end
- c
->buffer_ptr
;
1994 bytes_sent
= frame_bytes
- bytes_left
;
1995 return c
->cur_pts
+ (c
->cur_frame_duration
* bytes_sent
) / frame_bytes
;
2000 static int http_prepare_data(HTTPContext
*c
)
2003 AVFormatContext
*ctx
;
2005 av_freep(&c
->pb_buffer
);
2007 case HTTPSTATE_SEND_DATA_HEADER
:
2008 memset(&c
->fmt_ctx
, 0, sizeof(c
->fmt_ctx
));
2009 av_strlcpy(c
->fmt_ctx
.author
, c
->stream
->author
,
2010 sizeof(c
->fmt_ctx
.author
));
2011 av_strlcpy(c
->fmt_ctx
.comment
, c
->stream
->comment
,
2012 sizeof(c
->fmt_ctx
.comment
));
2013 av_strlcpy(c
->fmt_ctx
.copyright
, c
->stream
->copyright
,
2014 sizeof(c
->fmt_ctx
.copyright
));
2015 av_strlcpy(c
->fmt_ctx
.title
, c
->stream
->title
,
2016 sizeof(c
->fmt_ctx
.title
));
2018 /* open output stream by using specified codecs */
2019 c
->fmt_ctx
.oformat
= c
->stream
->fmt
;
2020 c
->fmt_ctx
.nb_streams
= c
->stream
->nb_streams
;
2021 for(i
=0;i
<c
->fmt_ctx
.nb_streams
;i
++) {
2024 st
= av_mallocz(sizeof(AVStream
));
2025 st
->codec
= avcodec_alloc_context();
2026 c
->fmt_ctx
.streams
[i
] = st
;
2027 /* if file or feed, then just take streams from FFStream struct */
2028 if (!c
->stream
->feed
||
2029 c
->stream
->feed
== c
->stream
)
2030 src
= c
->stream
->streams
[i
];
2032 src
= c
->stream
->feed
->streams
[c
->stream
->feed_streams
[i
]];
2036 st
->codec
->frame_number
= 0; /* XXX: should be done in
2037 AVStream, not in codec */
2038 /* I'm pretty sure that this is not correct...
2039 * However, without it, we crash
2041 st
->codec
->coded_frame
= &dummy_frame
;
2043 c
->got_key_frame
= 0;
2045 /* prepare header and save header data in a stream */
2046 if (url_open_dyn_buf(&c
->fmt_ctx
.pb
) < 0) {
2047 /* XXX: potential leak */
2050 c
->fmt_ctx
.pb
.is_streamed
= 1;
2052 av_set_parameters(&c
->fmt_ctx
, NULL
);
2053 if (av_write_header(&c
->fmt_ctx
) < 0)
2056 len
= url_close_dyn_buf(&c
->fmt_ctx
.pb
, &c
->pb_buffer
);
2057 c
->buffer_ptr
= c
->pb_buffer
;
2058 c
->buffer_end
= c
->pb_buffer
+ len
;
2060 c
->state
= HTTPSTATE_SEND_DATA
;
2061 c
->last_packet_sent
= 0;
2063 case HTTPSTATE_SEND_DATA
:
2064 /* find a new packet */
2068 /* read a packet from the input stream */
2069 if (c
->stream
->feed
) {
2070 ffm_set_write_index(c
->fmt_in
,
2071 c
->stream
->feed
->feed_write_index
,
2072 c
->stream
->feed
->feed_size
);
2075 if (c
->stream
->max_time
&&
2076 c
->stream
->max_time
+ c
->start_time
- cur_time
< 0) {
2077 /* We have timed out */
2078 c
->state
= HTTPSTATE_SEND_DATA_TRAILER
;
2081 if (av_read_frame(c
->fmt_in
, &pkt
) < 0) {
2082 if (c
->stream
->feed
&& c
->stream
->feed
->feed_opened
) {
2083 /* if coming from feed, it means we reached the end of the
2084 ffm file, so must wait for more data */
2085 c
->state
= HTTPSTATE_WAIT_FEED
;
2086 return 1; /* state changed */
2088 if (c
->stream
->loop
) {
2089 av_close_input_file(c
->fmt_in
);
2091 if (open_input_stream(c
, "") < 0)
2096 /* must send trailer now because eof or error */
2097 c
->state
= HTTPSTATE_SEND_DATA_TRAILER
;
2101 /* update first pts if needed */
2102 if (c
->first_pts
== AV_NOPTS_VALUE
) {
2103 c
->first_pts
= av_rescale_q(pkt
.dts
, c
->fmt_in
->streams
[pkt
.stream_index
]->time_base
, AV_TIME_BASE_Q
);
2104 c
->start_time
= cur_time
;
2106 /* send it to the appropriate stream */
2107 if (c
->stream
->feed
) {
2108 /* if coming from a feed, select the right stream */
2109 if (c
->switch_pending
) {
2110 c
->switch_pending
= 0;
2111 for(i
=0;i
<c
->stream
->nb_streams
;i
++) {
2112 if (c
->switch_feed_streams
[i
] == pkt
.stream_index
) {
2113 if (pkt
.flags
& PKT_FLAG_KEY
) {
2114 do_switch_stream(c
, i
);
2117 if (c
->switch_feed_streams
[i
] >= 0) {
2118 c
->switch_pending
= 1;
2122 for(i
=0;i
<c
->stream
->nb_streams
;i
++) {
2123 if (c
->feed_streams
[i
] == pkt
.stream_index
) {
2124 pkt
.stream_index
= i
;
2125 if (pkt
.flags
& PKT_FLAG_KEY
) {
2126 c
->got_key_frame
|= 1 << i
;
2128 /* See if we have all the key frames, then
2129 * we start to send. This logic is not quite
2130 * right, but it works for the case of a
2131 * single video stream with one or more
2132 * audio streams (for which every frame is
2133 * typically a key frame).
2135 if (!c
->stream
->send_on_key
||
2136 ((c
->got_key_frame
+ 1) >> c
->stream
->nb_streams
)) {
2142 AVCodecContext
*codec
;
2145 /* specific handling for RTP: we use several
2146 output stream (one for each RTP
2147 connection). XXX: need more abstract handling */
2148 if (c
->is_packetized
) {
2150 /* compute send time and duration */
2151 st
= c
->fmt_in
->streams
[pkt
.stream_index
];
2152 c
->cur_pts
= av_rescale_q(pkt
.dts
, st
->time_base
, AV_TIME_BASE_Q
);
2153 if (st
->start_time
!= AV_NOPTS_VALUE
)
2154 c
->cur_pts
-= av_rescale_q(st
->start_time
, st
->time_base
, AV_TIME_BASE_Q
);
2155 c
->cur_frame_duration
= av_rescale_q(pkt
.duration
, st
->time_base
, AV_TIME_BASE_Q
);
2157 printf("index=%d pts=%0.3f duration=%0.6f\n",
2159 (double)c
->cur_pts
/
2161 (double)c
->cur_frame_duration
/
2164 /* find RTP context */
2165 c
->packet_stream_index
= pkt
.stream_index
;
2166 ctx
= c
->rtp_ctx
[c
->packet_stream_index
];
2168 av_free_packet(&pkt
);
2171 codec
= ctx
->streams
[0]->codec
;
2172 /* only one stream per RTP connection */
2173 pkt
.stream_index
= 0;
2177 codec
= ctx
->streams
[pkt
.stream_index
]->codec
;
2180 codec
->coded_frame
->key_frame
= ((pkt
.flags
& PKT_FLAG_KEY
) != 0);
2181 if (c
->is_packetized
) {
2182 int max_packet_size
;
2183 if (c
->rtp_protocol
== RTSP_PROTOCOL_RTP_TCP
)
2184 max_packet_size
= RTSP_TCP_MAX_PACKET_SIZE
;
2186 max_packet_size
= url_get_max_packet_size(c
->rtp_handles
[c
->packet_stream_index
]);
2187 ret
= url_open_dyn_packet_buf(&ctx
->pb
, max_packet_size
);
2189 ret
= url_open_dyn_buf(&ctx
->pb
);
2192 /* XXX: potential leak */
2195 if (pkt
.dts
!= AV_NOPTS_VALUE
)
2196 pkt
.dts
= av_rescale_q(pkt
.dts
,
2197 c
->fmt_in
->streams
[pkt
.stream_index
]->time_base
,
2198 ctx
->streams
[pkt
.stream_index
]->time_base
);
2199 if (pkt
.pts
!= AV_NOPTS_VALUE
)
2200 pkt
.pts
= av_rescale_q(pkt
.pts
,
2201 c
->fmt_in
->streams
[pkt
.stream_index
]->time_base
,
2202 ctx
->streams
[pkt
.stream_index
]->time_base
);
2203 if (av_write_frame(ctx
, &pkt
)) {
2204 c
->state
= HTTPSTATE_SEND_DATA_TRAILER
;
2207 len
= url_close_dyn_buf(&ctx
->pb
, &c
->pb_buffer
);
2208 c
->cur_frame_bytes
= len
;
2209 c
->buffer_ptr
= c
->pb_buffer
;
2210 c
->buffer_end
= c
->pb_buffer
+ len
;
2212 codec
->frame_number
++;
2216 av_free_packet(&pkt
);
2222 case HTTPSTATE_SEND_DATA_TRAILER
:
2223 /* last packet test ? */
2224 if (c
->last_packet_sent
|| c
->is_packetized
)
2227 /* prepare header */
2228 if (url_open_dyn_buf(&ctx
->pb
) < 0) {
2229 /* XXX: potential leak */
2232 av_write_trailer(ctx
);
2233 len
= url_close_dyn_buf(&ctx
->pb
, &c
->pb_buffer
);
2234 c
->buffer_ptr
= c
->pb_buffer
;
2235 c
->buffer_end
= c
->pb_buffer
+ len
;
2237 c
->last_packet_sent
= 1;
2243 /* should convert the format at the same time */
2244 /* send data starting at c->buffer_ptr to the output connection
2245 (either UDP or TCP connection) */
2246 static int http_send_data(HTTPContext
*c
)
2251 if (c
->buffer_ptr
>= c
->buffer_end
) {
2252 ret
= http_prepare_data(c
);
2255 else if (ret
!= 0) {
2256 /* state change requested */
2260 if (c
->is_packetized
) {
2261 /* RTP data output */
2262 len
= c
->buffer_end
- c
->buffer_ptr
;
2264 /* fail safe - should never happen */
2266 c
->buffer_ptr
= c
->buffer_end
;
2269 len
= (c
->buffer_ptr
[0] << 24) |
2270 (c
->buffer_ptr
[1] << 16) |
2271 (c
->buffer_ptr
[2] << 8) |
2273 if (len
> (c
->buffer_end
- c
->buffer_ptr
))
2275 if ((get_packet_send_clock(c
) - get_server_clock(c
)) > 0) {
2276 /* nothing to send yet: we can wait */
2280 c
->data_count
+= len
;
2281 update_datarate(&c
->datarate
, c
->data_count
);
2283 c
->stream
->bytes_served
+= len
;
2285 if (c
->rtp_protocol
== RTSP_PROTOCOL_RTP_TCP
) {
2286 /* RTP packets are sent inside the RTSP TCP connection */
2287 ByteIOContext pb1
, *pb
= &pb1
;
2288 int interleaved_index
, size
;
2290 HTTPContext
*rtsp_c
;
2293 /* if no RTSP connection left, error */
2296 /* if already sending something, then wait. */
2297 if (rtsp_c
->state
!= RTSPSTATE_WAIT_REQUEST
) {
2300 if (url_open_dyn_buf(pb
) < 0)
2302 interleaved_index
= c
->packet_stream_index
* 2;
2303 /* RTCP packets are sent at odd indexes */
2304 if (c
->buffer_ptr
[1] == 200)
2305 interleaved_index
++;
2306 /* write RTSP TCP header */
2308 header
[1] = interleaved_index
;
2309 header
[2] = len
>> 8;
2311 put_buffer(pb
, header
, 4);
2312 /* write RTP packet data */
2314 put_buffer(pb
, c
->buffer_ptr
, len
);
2315 size
= url_close_dyn_buf(pb
, &c
->packet_buffer
);
2316 /* prepare asynchronous TCP sending */
2317 rtsp_c
->packet_buffer_ptr
= c
->packet_buffer
;
2318 rtsp_c
->packet_buffer_end
= c
->packet_buffer
+ size
;
2319 c
->buffer_ptr
+= len
;
2321 /* send everything we can NOW */
2322 len
= send(rtsp_c
->fd
, rtsp_c
->packet_buffer_ptr
,
2323 rtsp_c
->packet_buffer_end
- rtsp_c
->packet_buffer_ptr
, 0);
2325 rtsp_c
->packet_buffer_ptr
+= len
;
2327 if (rtsp_c
->packet_buffer_ptr
< rtsp_c
->packet_buffer_end
) {
2328 /* if we could not send all the data, we will
2329 send it later, so a new state is needed to
2330 "lock" the RTSP TCP connection */
2331 rtsp_c
->state
= RTSPSTATE_SEND_PACKET
;
2334 /* all data has been sent */
2335 av_freep(&c
->packet_buffer
);
2338 /* send RTP packet directly in UDP */
2340 url_write(c
->rtp_handles
[c
->packet_stream_index
],
2341 c
->buffer_ptr
, len
);
2342 c
->buffer_ptr
+= len
;
2343 /* here we continue as we can send several packets per 10 ms slot */
2346 /* TCP data output */
2347 len
= send(c
->fd
, c
->buffer_ptr
, c
->buffer_end
- c
->buffer_ptr
, 0);
2349 if (ff_neterrno() != FF_NETERROR(EAGAIN
) &&
2350 ff_neterrno() != FF_NETERROR(EINTR
)) {
2351 /* error : close connection */
2357 c
->buffer_ptr
+= len
;
2359 c
->data_count
+= len
;
2360 update_datarate(&c
->datarate
, c
->data_count
);
2362 c
->stream
->bytes_served
+= len
;
2370 static int http_start_receive_data(HTTPContext
*c
)
2374 if (c
->stream
->feed_opened
)
2377 /* Don't permit writing to this one */
2378 if (c
->stream
->readonly
)
2382 fd
= open(c
->stream
->feed_filename
, O_RDWR
);
2387 c
->stream
->feed_write_index
= ffm_read_write_index(fd
);
2388 c
->stream
->feed_size
= lseek(fd
, 0, SEEK_END
);
2389 lseek(fd
, 0, SEEK_SET
);
2391 /* init buffer input */
2392 c
->buffer_ptr
= c
->buffer
;
2393 c
->buffer_end
= c
->buffer
+ FFM_PACKET_SIZE
;
2394 c
->stream
->feed_opened
= 1;
2398 static int http_receive_data(HTTPContext
*c
)
2402 if (c
->buffer_end
> c
->buffer_ptr
) {
2405 len
= recv(c
->fd
, c
->buffer_ptr
, c
->buffer_end
- c
->buffer_ptr
, 0);
2407 if (ff_neterrno() != FF_NETERROR(EAGAIN
) &&
2408 ff_neterrno() != FF_NETERROR(EINTR
)) {
2409 /* error : close connection */
2412 } else if (len
== 0) {
2413 /* end of connection : close it */
2416 c
->buffer_ptr
+= len
;
2417 c
->data_count
+= len
;
2418 update_datarate(&c
->datarate
, c
->data_count
);
2422 if (c
->buffer_ptr
- c
->buffer
>= 2 && c
->data_count
> FFM_PACKET_SIZE
) {
2423 if (c
->buffer
[0] != 'f' ||
2424 c
->buffer
[1] != 'm') {
2425 http_log("Feed stream has become desynchronized -- disconnecting\n");
2430 if (c
->buffer_ptr
>= c
->buffer_end
) {
2431 FFStream
*feed
= c
->stream
;
2432 /* a packet has been received : write it in the store, except
2434 if (c
->data_count
> FFM_PACKET_SIZE
) {
2436 // printf("writing pos=0x%"PRIx64" size=0x%"PRIx64"\n", feed->feed_write_index, feed->feed_size);
2437 /* XXX: use llseek or url_seek */
2438 lseek(c
->feed_fd
, feed
->feed_write_index
, SEEK_SET
);
2439 write(c
->feed_fd
, c
->buffer
, FFM_PACKET_SIZE
);
2441 feed
->feed_write_index
+= FFM_PACKET_SIZE
;
2442 /* update file size */
2443 if (feed
->feed_write_index
> c
->stream
->feed_size
)
2444 feed
->feed_size
= feed
->feed_write_index
;
2446 /* handle wrap around if max file size reached */
2447 if (c
->stream
->feed_max_size
&& feed
->feed_write_index
>= c
->stream
->feed_max_size
)
2448 feed
->feed_write_index
= FFM_PACKET_SIZE
;
2451 ffm_write_write_index(c
->feed_fd
, feed
->feed_write_index
);
2453 /* wake up any waiting connections */
2454 for(c1
= first_http_ctx
; c1
!= NULL
; c1
= c1
->next
) {
2455 if (c1
->state
== HTTPSTATE_WAIT_FEED
&&
2456 c1
->stream
->feed
== c
->stream
->feed
) {
2457 c1
->state
= HTTPSTATE_SEND_DATA
;
2461 /* We have a header in our hands that contains useful data */
2463 AVInputFormat
*fmt_in
;
2464 ByteIOContext
*pb
= &s
.pb
;
2467 memset(&s
, 0, sizeof(s
));
2469 url_open_buf(pb
, c
->buffer
, c
->buffer_end
- c
->buffer
, URL_RDONLY
);
2470 pb
->buf_end
= c
->buffer_end
; /* ?? */
2471 pb
->is_streamed
= 1;
2473 /* use feed output format name to find corresponding input format */
2474 fmt_in
= av_find_input_format(feed
->fmt
->name
);
2478 if (fmt_in
->priv_data_size
> 0) {
2479 s
.priv_data
= av_mallocz(fmt_in
->priv_data_size
);
2485 if (fmt_in
->read_header(&s
, 0) < 0) {
2486 av_freep(&s
.priv_data
);
2490 /* Now we have the actual streams */
2491 if (s
.nb_streams
!= feed
->nb_streams
) {
2492 av_freep(&s
.priv_data
);
2495 for (i
= 0; i
< s
.nb_streams
; i
++) {
2496 memcpy(feed
->streams
[i
]->codec
,
2497 s
.streams
[i
]->codec
, sizeof(AVCodecContext
));
2499 av_freep(&s
.priv_data
);
2501 c
->buffer_ptr
= c
->buffer
;
2506 c
->stream
->feed_opened
= 0;
2511 /********************************************************************/
2514 static void rtsp_reply_header(HTTPContext
*c
, enum RTSPStatusCode error_number
)
2521 switch(error_number
) {
2522 case RTSP_STATUS_OK
:
2525 case RTSP_STATUS_METHOD
:
2526 str
= "Method Not Allowed";
2528 case RTSP_STATUS_BANDWIDTH
:
2529 str
= "Not Enough Bandwidth";
2531 case RTSP_STATUS_SESSION
:
2532 str
= "Session Not Found";
2534 case RTSP_STATUS_STATE
:
2535 str
= "Method Not Valid in This State";
2537 case RTSP_STATUS_AGGREGATE
:
2538 str
= "Aggregate operation not allowed";
2540 case RTSP_STATUS_ONLY_AGGREGATE
:
2541 str
= "Only aggregate operation allowed";
2543 case RTSP_STATUS_TRANSPORT
:
2544 str
= "Unsupported transport";
2546 case RTSP_STATUS_INTERNAL
:
2547 str
= "Internal Server Error";
2549 case RTSP_STATUS_SERVICE
:
2550 str
= "Service Unavailable";
2552 case RTSP_STATUS_VERSION
:
2553 str
= "RTSP Version not supported";
2556 str
= "Unknown Error";
2560 url_fprintf(c
->pb
, "RTSP/1.0 %d %s\r\n", error_number
, str
);
2561 url_fprintf(c
->pb
, "CSeq: %d\r\n", c
->seq
);
2563 /* output GMT time */
2567 p
= buf2
+ strlen(p
) - 1;
2570 url_fprintf(c
->pb
, "Date: %s GMT\r\n", buf2
);
2573 static void rtsp_reply_error(HTTPContext
*c
, enum RTSPStatusCode error_number
)
2575 rtsp_reply_header(c
, error_number
);
2576 url_fprintf(c
->pb
, "\r\n");
2579 static int rtsp_parse_request(HTTPContext
*c
)
2581 const char *p
, *p1
, *p2
;
2588 RTSPHeader header1
, *header
= &header1
;
2590 c
->buffer_ptr
[0] = '\0';
2593 get_word(cmd
, sizeof(cmd
), &p
);
2594 get_word(url
, sizeof(url
), &p
);
2595 get_word(protocol
, sizeof(protocol
), &p
);
2597 av_strlcpy(c
->method
, cmd
, sizeof(c
->method
));
2598 av_strlcpy(c
->url
, url
, sizeof(c
->url
));
2599 av_strlcpy(c
->protocol
, protocol
, sizeof(c
->protocol
));
2602 if (url_open_dyn_buf(c
->pb
) < 0) {
2603 /* XXX: cannot do more */
2604 c
->pb
= NULL
; /* safety */
2608 /* check version name */
2609 if (strcmp(protocol
, "RTSP/1.0") != 0) {
2610 rtsp_reply_error(c
, RTSP_STATUS_VERSION
);
2614 /* parse each header line */
2615 memset(header
, 0, sizeof(RTSPHeader
));
2616 /* skip to next line */
2617 while (*p
!= '\n' && *p
!= '\0')
2621 while (*p
!= '\0') {
2622 p1
= strchr(p
, '\n');
2626 if (p2
> p
&& p2
[-1] == '\r')
2628 /* skip empty line */
2632 if (len
> sizeof(line
) - 1)
2633 len
= sizeof(line
) - 1;
2634 memcpy(line
, p
, len
);
2636 rtsp_parse_line(header
, line
);
2640 /* handle sequence number */
2641 c
->seq
= header
->seq
;
2643 if (!strcmp(cmd
, "DESCRIBE")) {
2644 rtsp_cmd_describe(c
, url
);
2645 } else if (!strcmp(cmd
, "OPTIONS")) {
2646 rtsp_cmd_options(c
, url
);
2647 } else if (!strcmp(cmd
, "SETUP")) {
2648 rtsp_cmd_setup(c
, url
, header
);
2649 } else if (!strcmp(cmd
, "PLAY")) {
2650 rtsp_cmd_play(c
, url
, header
);
2651 } else if (!strcmp(cmd
, "PAUSE")) {
2652 rtsp_cmd_pause(c
, url
, header
);
2653 } else if (!strcmp(cmd
, "TEARDOWN")) {
2654 rtsp_cmd_teardown(c
, url
, header
);
2656 rtsp_reply_error(c
, RTSP_STATUS_METHOD
);
2659 len
= url_close_dyn_buf(c
->pb
, &c
->pb_buffer
);
2660 c
->pb
= NULL
; /* safety */
2662 /* XXX: cannot do more */
2665 c
->buffer_ptr
= c
->pb_buffer
;
2666 c
->buffer_end
= c
->pb_buffer
+ len
;
2667 c
->state
= RTSPSTATE_SEND_REPLY
;
2671 /* XXX: move that to rtsp.c, but would need to replace FFStream by
2673 static int prepare_sdp_description(FFStream
*stream
, uint8_t **pbuffer
,
2674 struct in_addr my_ip
)
2676 ByteIOContext pb1
, *pb
= &pb1
;
2677 int i
, payload_type
, port
, private_payload_type
, j
;
2678 const char *ipstr
, *title
, *mediatype
;
2681 if (url_open_dyn_buf(pb
) < 0)
2684 /* general media info */
2686 url_fprintf(pb
, "v=0\n");
2687 ipstr
= inet_ntoa(my_ip
);
2688 url_fprintf(pb
, "o=- 0 0 IN IP4 %s\n", ipstr
);
2689 title
= stream
->title
;
2690 if (title
[0] == '\0')
2692 url_fprintf(pb
, "s=%s\n", title
);
2693 if (stream
->comment
[0] != '\0')
2694 url_fprintf(pb
, "i=%s\n", stream
->comment
);
2695 if (stream
->is_multicast
) {
2696 url_fprintf(pb
, "c=IN IP4 %s\n", inet_ntoa(stream
->multicast_ip
));
2698 /* for each stream, we output the necessary info */
2699 private_payload_type
= RTP_PT_PRIVATE
;
2700 for(i
= 0; i
< stream
->nb_streams
; i
++) {
2701 st
= stream
->streams
[i
];
2702 if (st
->codec
->codec_id
== CODEC_ID_MPEG2TS
) {
2703 mediatype
= "video";
2705 switch(st
->codec
->codec_type
) {
2706 case CODEC_TYPE_AUDIO
:
2707 mediatype
= "audio";
2709 case CODEC_TYPE_VIDEO
:
2710 mediatype
= "video";
2713 mediatype
= "application";
2717 /* NOTE: the port indication is not correct in case of
2718 unicast. It is not an issue because RTSP gives it */
2719 payload_type
= rtp_get_payload_type(st
->codec
);
2720 if (payload_type
< 0)
2721 payload_type
= private_payload_type
++;
2722 if (stream
->is_multicast
) {
2723 port
= stream
->multicast_port
+ 2 * i
;
2727 url_fprintf(pb
, "m=%s %d RTP/AVP %d\n",
2728 mediatype
, port
, payload_type
);
2729 if (payload_type
>= RTP_PT_PRIVATE
) {
2730 /* for private payload type, we need to give more info */
2731 switch(st
->codec
->codec_id
) {
2732 case CODEC_ID_MPEG4
:
2735 url_fprintf(pb
, "a=rtpmap:%d MP4V-ES/%d\n",
2736 payload_type
, 90000);
2737 /* we must also add the mpeg4 header */
2738 data
= st
->codec
->extradata
;
2740 url_fprintf(pb
, "a=fmtp:%d config=", payload_type
);
2741 for(j
=0;j
<st
->codec
->extradata_size
;j
++) {
2742 url_fprintf(pb
, "%02x", data
[j
]);
2744 url_fprintf(pb
, "\n");
2749 /* XXX: add other codecs ? */
2753 url_fprintf(pb
, "a=control:streamid=%d\n", i
);
2755 return url_close_dyn_buf(pb
, pbuffer
);
2757 url_close_dyn_buf(pb
, pbuffer
);
2762 static void rtsp_cmd_options(HTTPContext
*c
, const char *url
)
2764 // rtsp_reply_header(c, RTSP_STATUS_OK);
2765 url_fprintf(c
->pb
, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK
, "OK");
2766 url_fprintf(c
->pb
, "CSeq: %d\r\n", c
->seq
);
2767 url_fprintf(c
->pb
, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
2768 url_fprintf(c
->pb
, "\r\n");
2771 static void rtsp_cmd_describe(HTTPContext
*c
, const char *url
)
2777 int content_length
, len
;
2778 struct sockaddr_in my_addr
;
2780 /* find which url is asked */
2781 url_split(NULL
, 0, NULL
, 0, NULL
, 0, NULL
, path1
, sizeof(path1
), url
);
2786 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
2787 if (!stream
->is_feed
&& stream
->fmt
== &rtp_muxer
&&
2788 !strcmp(path
, stream
->filename
)) {
2792 /* no stream found */
2793 rtsp_reply_error(c
, RTSP_STATUS_SERVICE
); /* XXX: right error ? */
2797 /* prepare the media description in sdp format */
2799 /* get the host IP */
2800 len
= sizeof(my_addr
);
2801 getsockname(c
->fd
, (struct sockaddr
*)&my_addr
, &len
);
2802 content_length
= prepare_sdp_description(stream
, &content
, my_addr
.sin_addr
);
2803 if (content_length
< 0) {
2804 rtsp_reply_error(c
, RTSP_STATUS_INTERNAL
);
2807 rtsp_reply_header(c
, RTSP_STATUS_OK
);
2808 url_fprintf(c
->pb
, "Content-Type: application/sdp\r\n");
2809 url_fprintf(c
->pb
, "Content-Length: %d\r\n", content_length
);
2810 url_fprintf(c
->pb
, "\r\n");
2811 put_buffer(c
->pb
, content
, content_length
);
2814 static HTTPContext
*find_rtp_session(const char *session_id
)
2818 if (session_id
[0] == '\0')
2821 for(c
= first_http_ctx
; c
!= NULL
; c
= c
->next
) {
2822 if (!strcmp(c
->session_id
, session_id
))
2828 static RTSPTransportField
*find_transport(RTSPHeader
*h
, enum RTSPProtocol protocol
)
2830 RTSPTransportField
*th
;
2833 for(i
=0;i
<h
->nb_transports
;i
++) {
2834 th
= &h
->transports
[i
];
2835 if (th
->protocol
== protocol
)
2841 static void rtsp_cmd_setup(HTTPContext
*c
, const char *url
,
2845 int stream_index
, port
;
2850 RTSPTransportField
*th
;
2851 struct sockaddr_in dest_addr
;
2852 RTSPActionServerSetup setup
;
2854 /* find which url is asked */
2855 url_split(NULL
, 0, NULL
, 0, NULL
, 0, NULL
, path1
, sizeof(path1
), url
);
2860 /* now check each stream */
2861 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
2862 if (!stream
->is_feed
&& stream
->fmt
== &rtp_muxer
) {
2863 /* accept aggregate filenames only if single stream */
2864 if (!strcmp(path
, stream
->filename
)) {
2865 if (stream
->nb_streams
!= 1) {
2866 rtsp_reply_error(c
, RTSP_STATUS_AGGREGATE
);
2873 for(stream_index
= 0; stream_index
< stream
->nb_streams
;
2875 snprintf(buf
, sizeof(buf
), "%s/streamid=%d",
2876 stream
->filename
, stream_index
);
2877 if (!strcmp(path
, buf
))
2882 /* no stream found */
2883 rtsp_reply_error(c
, RTSP_STATUS_SERVICE
); /* XXX: right error ? */
2887 /* generate session id if needed */
2888 if (h
->session_id
[0] == '\0') {
2889 snprintf(h
->session_id
, sizeof(h
->session_id
), "%08x%08x",
2890 av_random(&random_state
), av_random(&random_state
));
2893 /* find rtp session, and create it if none found */
2894 rtp_c
= find_rtp_session(h
->session_id
);
2896 /* always prefer UDP */
2897 th
= find_transport(h
, RTSP_PROTOCOL_RTP_UDP
);
2899 th
= find_transport(h
, RTSP_PROTOCOL_RTP_TCP
);
2901 rtsp_reply_error(c
, RTSP_STATUS_TRANSPORT
);
2906 rtp_c
= rtp_new_connection(&c
->from_addr
, stream
, h
->session_id
,
2909 rtsp_reply_error(c
, RTSP_STATUS_BANDWIDTH
);
2913 /* open input stream */
2914 if (open_input_stream(rtp_c
, "") < 0) {
2915 rtsp_reply_error(c
, RTSP_STATUS_INTERNAL
);
2920 /* test if stream is OK (test needed because several SETUP needs
2921 to be done for a given file) */
2922 if (rtp_c
->stream
!= stream
) {
2923 rtsp_reply_error(c
, RTSP_STATUS_SERVICE
);
2927 /* test if stream is already set up */
2928 if (rtp_c
->rtp_ctx
[stream_index
]) {
2929 rtsp_reply_error(c
, RTSP_STATUS_STATE
);
2933 /* check transport */
2934 th
= find_transport(h
, rtp_c
->rtp_protocol
);
2935 if (!th
|| (th
->protocol
== RTSP_PROTOCOL_RTP_UDP
&&
2936 th
->client_port_min
<= 0)) {
2937 rtsp_reply_error(c
, RTSP_STATUS_TRANSPORT
);
2941 /* setup default options */
2942 setup
.transport_option
[0] = '\0';
2943 dest_addr
= rtp_c
->from_addr
;
2944 dest_addr
.sin_port
= htons(th
->client_port_min
);
2947 if (rtp_new_av_stream(rtp_c
, stream_index
, &dest_addr
, c
) < 0) {
2948 rtsp_reply_error(c
, RTSP_STATUS_TRANSPORT
);
2952 /* now everything is OK, so we can send the connection parameters */
2953 rtsp_reply_header(c
, RTSP_STATUS_OK
);
2955 url_fprintf(c
->pb
, "Session: %s\r\n", rtp_c
->session_id
);
2957 switch(rtp_c
->rtp_protocol
) {
2958 case RTSP_PROTOCOL_RTP_UDP
:
2959 port
= rtp_get_local_port(rtp_c
->rtp_handles
[stream_index
]);
2960 url_fprintf(c
->pb
, "Transport: RTP/AVP/UDP;unicast;"
2961 "client_port=%d-%d;server_port=%d-%d",
2962 th
->client_port_min
, th
->client_port_min
+ 1,
2965 case RTSP_PROTOCOL_RTP_TCP
:
2966 url_fprintf(c
->pb
, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
2967 stream_index
* 2, stream_index
* 2 + 1);
2972 if (setup
.transport_option
[0] != '\0') {
2973 url_fprintf(c
->pb
, ";%s", setup
.transport_option
);
2975 url_fprintf(c
->pb
, "\r\n");
2978 url_fprintf(c
->pb
, "\r\n");
2982 /* find an rtp connection by using the session ID. Check consistency
2984 static HTTPContext
*find_rtp_session_with_url(const char *url
,
2985 const char *session_id
)
2993 rtp_c
= find_rtp_session(session_id
);
2997 /* find which url is asked */
2998 url_split(NULL
, 0, NULL
, 0, NULL
, 0, NULL
, path1
, sizeof(path1
), url
);
3002 if(!strcmp(path
, rtp_c
->stream
->filename
)) return rtp_c
;
3003 for(s
=0; s
<rtp_c
->stream
->nb_streams
; ++s
) {
3004 snprintf(buf
, sizeof(buf
), "%s/streamid=%d",
3005 rtp_c
->stream
->filename
, s
);
3006 if(!strncmp(path
, buf
, sizeof(buf
))) {
3007 // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
3014 static void rtsp_cmd_play(HTTPContext
*c
, const char *url
, RTSPHeader
*h
)
3018 rtp_c
= find_rtp_session_with_url(url
, h
->session_id
);
3020 rtsp_reply_error(c
, RTSP_STATUS_SESSION
);
3024 if (rtp_c
->state
!= HTTPSTATE_SEND_DATA
&&
3025 rtp_c
->state
!= HTTPSTATE_WAIT_FEED
&&
3026 rtp_c
->state
!= HTTPSTATE_READY
) {
3027 rtsp_reply_error(c
, RTSP_STATUS_STATE
);
3032 /* XXX: seek in stream */
3033 if (h
->range_start
!= AV_NOPTS_VALUE
) {
3034 printf("range_start=%0.3f\n", (double)h
->range_start
/ AV_TIME_BASE
);
3035 av_seek_frame(rtp_c
->fmt_in
, -1, h
->range_start
);
3039 rtp_c
->state
= HTTPSTATE_SEND_DATA
;
3041 /* now everything is OK, so we can send the connection parameters */
3042 rtsp_reply_header(c
, RTSP_STATUS_OK
);
3044 url_fprintf(c
->pb
, "Session: %s\r\n", rtp_c
->session_id
);
3045 url_fprintf(c
->pb
, "\r\n");
3048 static void rtsp_cmd_pause(HTTPContext
*c
, const char *url
, RTSPHeader
*h
)
3052 rtp_c
= find_rtp_session_with_url(url
, h
->session_id
);
3054 rtsp_reply_error(c
, RTSP_STATUS_SESSION
);
3058 if (rtp_c
->state
!= HTTPSTATE_SEND_DATA
&&
3059 rtp_c
->state
!= HTTPSTATE_WAIT_FEED
) {
3060 rtsp_reply_error(c
, RTSP_STATUS_STATE
);
3064 rtp_c
->state
= HTTPSTATE_READY
;
3065 rtp_c
->first_pts
= AV_NOPTS_VALUE
;
3066 /* now everything is OK, so we can send the connection parameters */
3067 rtsp_reply_header(c
, RTSP_STATUS_OK
);
3069 url_fprintf(c
->pb
, "Session: %s\r\n", rtp_c
->session_id
);
3070 url_fprintf(c
->pb
, "\r\n");
3073 static void rtsp_cmd_teardown(HTTPContext
*c
, const char *url
, RTSPHeader
*h
)
3076 char session_id
[32];
3078 rtp_c
= find_rtp_session_with_url(url
, h
->session_id
);
3080 rtsp_reply_error(c
, RTSP_STATUS_SESSION
);
3084 av_strlcpy(session_id
, rtp_c
->session_id
, sizeof(session_id
));
3086 /* abort the session */
3087 close_connection(rtp_c
);
3089 /* now everything is OK, so we can send the connection parameters */
3090 rtsp_reply_header(c
, RTSP_STATUS_OK
);
3092 url_fprintf(c
->pb
, "Session: %s\r\n", session_id
);
3093 url_fprintf(c
->pb
, "\r\n");
3097 /********************************************************************/
3100 static HTTPContext
*rtp_new_connection(struct sockaddr_in
*from_addr
,
3101 FFStream
*stream
, const char *session_id
,
3102 enum RTSPProtocol rtp_protocol
)
3104 HTTPContext
*c
= NULL
;
3105 const char *proto_str
;
3107 /* XXX: should output a warning page when coming
3108 close to the connection limit */
3109 if (nb_connections
>= nb_max_connections
)
3112 /* add a new connection */
3113 c
= av_mallocz(sizeof(HTTPContext
));
3118 c
->poll_entry
= NULL
;
3119 c
->from_addr
= *from_addr
;
3120 c
->buffer_size
= IOBUFFER_INIT_SIZE
;
3121 c
->buffer
= av_malloc(c
->buffer_size
);
3126 av_strlcpy(c
->session_id
, session_id
, sizeof(c
->session_id
));
3127 c
->state
= HTTPSTATE_READY
;
3128 c
->is_packetized
= 1;
3129 c
->rtp_protocol
= rtp_protocol
;
3131 /* protocol is shown in statistics */
3132 switch(c
->rtp_protocol
) {
3133 case RTSP_PROTOCOL_RTP_UDP_MULTICAST
:
3134 proto_str
= "MCAST";
3136 case RTSP_PROTOCOL_RTP_UDP
:
3139 case RTSP_PROTOCOL_RTP_TCP
:
3146 av_strlcpy(c
->protocol
, "RTP/", sizeof(c
->protocol
));
3147 av_strlcat(c
->protocol
, proto_str
, sizeof(c
->protocol
));
3149 current_bandwidth
+= stream
->bandwidth
;
3151 c
->next
= first_http_ctx
;
3163 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3164 command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
3166 static int rtp_new_av_stream(HTTPContext
*c
,
3167 int stream_index
, struct sockaddr_in
*dest_addr
,
3168 HTTPContext
*rtsp_c
)
3170 AVFormatContext
*ctx
;
3176 int max_packet_size
;
3178 /* now we can open the relevant output stream */
3179 ctx
= av_alloc_format_context();
3182 ctx
->oformat
= &rtp_muxer
;
3184 st
= av_mallocz(sizeof(AVStream
));
3187 st
->codec
= avcodec_alloc_context();
3188 ctx
->nb_streams
= 1;
3189 ctx
->streams
[0] = st
;
3191 if (!c
->stream
->feed
||
3192 c
->stream
->feed
== c
->stream
) {
3193 memcpy(st
, c
->stream
->streams
[stream_index
], sizeof(AVStream
));
3196 c
->stream
->feed
->streams
[c
->stream
->feed_streams
[stream_index
]],
3199 st
->priv_data
= NULL
;
3201 /* build destination RTP address */
3202 ipaddr
= inet_ntoa(dest_addr
->sin_addr
);
3204 switch(c
->rtp_protocol
) {
3205 case RTSP_PROTOCOL_RTP_UDP
:
3206 case RTSP_PROTOCOL_RTP_UDP_MULTICAST
:
3209 /* XXX: also pass as parameter to function ? */
3210 if (c
->stream
->is_multicast
) {
3212 ttl
= c
->stream
->multicast_ttl
;
3215 snprintf(ctx
->filename
, sizeof(ctx
->filename
),
3216 "rtp://%s:%d?multicast=1&ttl=%d",
3217 ipaddr
, ntohs(dest_addr
->sin_port
), ttl
);
3219 snprintf(ctx
->filename
, sizeof(ctx
->filename
),
3220 "rtp://%s:%d", ipaddr
, ntohs(dest_addr
->sin_port
));
3223 if (url_open(&h
, ctx
->filename
, URL_WRONLY
) < 0)
3225 c
->rtp_handles
[stream_index
] = h
;
3226 max_packet_size
= url_get_max_packet_size(h
);
3228 case RTSP_PROTOCOL_RTP_TCP
:
3231 max_packet_size
= RTSP_TCP_MAX_PACKET_SIZE
;
3237 http_log("%s:%d - - [%s] \"PLAY %s/streamid=%d %s\"\n",
3238 ipaddr
, ntohs(dest_addr
->sin_port
),
3240 c
->stream
->filename
, stream_index
, c
->protocol
);
3242 /* normally, no packets should be output here, but the packet size may be checked */
3243 if (url_open_dyn_packet_buf(&ctx
->pb
, max_packet_size
) < 0) {
3244 /* XXX: close stream */
3247 av_set_parameters(ctx
, NULL
);
3248 if (av_write_header(ctx
) < 0) {
3255 url_close_dyn_buf(&ctx
->pb
, &dummy_buf
);
3258 c
->rtp_ctx
[stream_index
] = ctx
;
3262 /********************************************************************/
3263 /* ffserver initialization */
3265 static AVStream
*add_av_stream1(FFStream
*stream
, AVCodecContext
*codec
)
3269 fst
= av_mallocz(sizeof(AVStream
));
3272 fst
->codec
= avcodec_alloc_context();
3273 fst
->priv_data
= av_mallocz(sizeof(FeedData
));
3274 memcpy(fst
->codec
, codec
, sizeof(AVCodecContext
));
3275 fst
->codec
->coded_frame
= &dummy_frame
;
3276 fst
->index
= stream
->nb_streams
;
3277 av_set_pts_info(fst
, 33, 1, 90000);
3278 stream
->streams
[stream
->nb_streams
++] = fst
;
3282 /* return the stream number in the feed */
3283 static int add_av_stream(FFStream
*feed
, AVStream
*st
)
3286 AVCodecContext
*av
, *av1
;
3290 for(i
=0;i
<feed
->nb_streams
;i
++) {
3291 st
= feed
->streams
[i
];
3293 if (av1
->codec_id
== av
->codec_id
&&
3294 av1
->codec_type
== av
->codec_type
&&
3295 av1
->bit_rate
== av
->bit_rate
) {
3297 switch(av
->codec_type
) {
3298 case CODEC_TYPE_AUDIO
:
3299 if (av1
->channels
== av
->channels
&&
3300 av1
->sample_rate
== av
->sample_rate
)
3303 case CODEC_TYPE_VIDEO
:
3304 if (av1
->width
== av
->width
&&
3305 av1
->height
== av
->height
&&
3306 av1
->time_base
.den
== av
->time_base
.den
&&
3307 av1
->time_base
.num
== av
->time_base
.num
&&
3308 av1
->gop_size
== av
->gop_size
)
3317 fst
= add_av_stream1(feed
, av
);
3320 return feed
->nb_streams
- 1;
3325 static void remove_stream(FFStream
*stream
)
3329 while (*ps
!= NULL
) {
3330 if (*ps
== stream
) {
3338 /* specific mpeg4 handling : we extract the raw parameters */
3339 static void extract_mpeg4_header(AVFormatContext
*infile
)
3341 int mpeg4_count
, i
, size
;
3347 for(i
=0;i
<infile
->nb_streams
;i
++) {
3348 st
= infile
->streams
[i
];
3349 if (st
->codec
->codec_id
== CODEC_ID_MPEG4
&&
3350 st
->codec
->extradata_size
== 0) {
3357 printf("MPEG4 without extra data: trying to find header in %s\n", infile
->filename
);
3358 while (mpeg4_count
> 0) {
3359 if (av_read_packet(infile
, &pkt
) < 0)
3361 st
= infile
->streams
[pkt
.stream_index
];
3362 if (st
->codec
->codec_id
== CODEC_ID_MPEG4
&&
3363 st
->codec
->extradata_size
== 0) {
3364 av_freep(&st
->codec
->extradata
);
3365 /* fill extradata with the header */
3366 /* XXX: we make hard suppositions here ! */
3368 while (p
< pkt
.data
+ pkt
.size
- 4) {
3369 /* stop when vop header is found */
3370 if (p
[0] == 0x00 && p
[1] == 0x00 &&
3371 p
[2] == 0x01 && p
[3] == 0xb6) {
3372 size
= p
- pkt
.data
;
3373 // av_hex_dump_log(infile, AV_LOG_DEBUG, pkt.data, size);
3374 st
->codec
->extradata
= av_malloc(size
);
3375 st
->codec
->extradata_size
= size
;
3376 memcpy(st
->codec
->extradata
, pkt
.data
, size
);
3383 av_free_packet(&pkt
);
3387 /* compute the needed AVStream for each file */
3388 static void build_file_streams(void)
3390 FFStream
*stream
, *stream_next
;
3391 AVFormatContext
*infile
;
3394 /* gather all streams */
3395 for(stream
= first_stream
; stream
!= NULL
; stream
= stream_next
) {
3396 stream_next
= stream
->next
;
3397 if (stream
->stream_type
== STREAM_TYPE_LIVE
&&
3399 /* the stream comes from a file */
3400 /* try to open the file */
3402 stream
->ap_in
= av_mallocz(sizeof(AVFormatParameters
));
3403 if (stream
->fmt
== &rtp_muxer
) {
3404 /* specific case : if transport stream output to RTP,
3405 we use a raw transport stream reader */
3406 stream
->ap_in
->mpeg2ts_raw
= 1;
3407 stream
->ap_in
->mpeg2ts_compute_pcr
= 1;
3410 if (av_open_input_file(&infile
, stream
->feed_filename
,
3411 stream
->ifmt
, 0, stream
->ap_in
) < 0) {
3412 http_log("%s not found", stream
->feed_filename
);
3413 /* remove stream (no need to spend more time on it) */
3415 remove_stream(stream
);
3417 /* find all the AVStreams inside and reference them in
3419 if (av_find_stream_info(infile
) < 0) {
3420 http_log("Could not find codec parameters from '%s'",
3421 stream
->feed_filename
);
3422 av_close_input_file(infile
);
3425 extract_mpeg4_header(infile
);
3427 for(i
=0;i
<infile
->nb_streams
;i
++) {
3428 add_av_stream1(stream
, infile
->streams
[i
]->codec
);
3430 av_close_input_file(infile
);
3436 /* compute the needed AVStream for each feed */
3437 static void build_feed_streams(void)
3439 FFStream
*stream
, *feed
;
3442 /* gather all streams */
3443 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
3444 feed
= stream
->feed
;
3446 if (!stream
->is_feed
) {
3447 /* we handle a stream coming from a feed */
3448 for(i
=0;i
<stream
->nb_streams
;i
++) {
3449 stream
->feed_streams
[i
] = add_av_stream(feed
, stream
->streams
[i
]);
3455 /* gather all streams */
3456 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
3457 feed
= stream
->feed
;
3459 if (stream
->is_feed
) {
3460 for(i
=0;i
<stream
->nb_streams
;i
++) {
3461 stream
->feed_streams
[i
] = i
;
3467 /* create feed files if needed */
3468 for(feed
= first_feed
; feed
!= NULL
; feed
= feed
->next_feed
) {
3471 if (url_exist(feed
->feed_filename
)) {
3472 /* See if it matches */
3476 if (av_open_input_file(&s
, feed
->feed_filename
, NULL
, FFM_PACKET_SIZE
, NULL
) >= 0) {
3477 /* Now see if it matches */
3478 if (s
->nb_streams
== feed
->nb_streams
) {
3480 for(i
=0;i
<s
->nb_streams
;i
++) {
3482 sf
= feed
->streams
[i
];
3485 if (sf
->index
!= ss
->index
||
3487 printf("Index & Id do not match for stream %d (%s)\n",
3488 i
, feed
->feed_filename
);
3491 AVCodecContext
*ccf
, *ccs
;
3495 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3497 if (CHECK_CODEC(codec
) || CHECK_CODEC(codec_type
)) {
3498 printf("Codecs do not match for stream %d\n", i
);
3500 } else if (CHECK_CODEC(bit_rate
) || CHECK_CODEC(flags
)) {
3501 printf("Codec bitrates do not match for stream %d\n", i
);
3503 } else if (ccf
->codec_type
== CODEC_TYPE_VIDEO
) {
3504 if (CHECK_CODEC(time_base
.den
) ||
3505 CHECK_CODEC(time_base
.num
) ||
3506 CHECK_CODEC(width
) ||
3507 CHECK_CODEC(height
)) {
3508 printf("Codec width, height and framerate do not match for stream %d\n", i
);
3511 } else if (ccf
->codec_type
== CODEC_TYPE_AUDIO
) {
3512 if (CHECK_CODEC(sample_rate
) ||
3513 CHECK_CODEC(channels
) ||
3514 CHECK_CODEC(frame_size
)) {
3515 printf("Codec sample_rate, channels, frame_size do not match for stream %d\n", i
);
3519 printf("Unknown codec type\n");
3528 printf("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3529 feed
->feed_filename
, s
->nb_streams
, feed
->nb_streams
);
3532 av_close_input_file(s
);
3534 printf("Deleting feed file '%s' as it appears to be corrupt\n",
3535 feed
->feed_filename
);
3538 if (feed
->readonly
) {
3539 printf("Unable to delete feed file '%s' as it is marked readonly\n",
3540 feed
->feed_filename
);
3543 unlink(feed
->feed_filename
);
3546 if (!url_exist(feed
->feed_filename
)) {
3547 AVFormatContext s1
, *s
= &s1
;
3549 if (feed
->readonly
) {
3550 printf("Unable to create feed file '%s' as it is marked readonly\n",
3551 feed
->feed_filename
);
3555 /* only write the header of the ffm file */
3556 if (url_fopen(&s
->pb
, feed
->feed_filename
, URL_WRONLY
) < 0) {
3557 fprintf(stderr
, "Could not open output feed file '%s'\n",
3558 feed
->feed_filename
);
3561 s
->oformat
= feed
->fmt
;
3562 s
->nb_streams
= feed
->nb_streams
;
3563 for(i
=0;i
<s
->nb_streams
;i
++) {
3565 st
= feed
->streams
[i
];
3568 av_set_parameters(s
, NULL
);
3569 if (av_write_header(s
) < 0) {
3570 fprintf(stderr
, "Container doesn't supports the required parameters\n");
3573 /* XXX: need better api */
3574 av_freep(&s
->priv_data
);
3577 /* get feed size and write index */
3578 fd
= open(feed
->feed_filename
, O_RDONLY
);
3580 fprintf(stderr
, "Could not open output feed file '%s'\n",
3581 feed
->feed_filename
);
3585 feed
->feed_write_index
= ffm_read_write_index(fd
);
3586 feed
->feed_size
= lseek(fd
, 0, SEEK_END
);
3587 /* ensure that we do not wrap before the end of file */
3588 if (feed
->feed_max_size
&& feed
->feed_max_size
< feed
->feed_size
)
3589 feed
->feed_max_size
= feed
->feed_size
;
3595 /* compute the bandwidth used by each stream */
3596 static void compute_bandwidth(void)
3601 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
3603 for(i
=0;i
<stream
->nb_streams
;i
++) {
3604 AVStream
*st
= stream
->streams
[i
];
3605 switch(st
->codec
->codec_type
) {
3606 case CODEC_TYPE_AUDIO
:
3607 case CODEC_TYPE_VIDEO
:
3608 bandwidth
+= st
->codec
->bit_rate
;
3614 stream
->bandwidth
= (bandwidth
+ 999) / 1000;
3618 static void get_arg(char *buf
, int buf_size
, const char **pp
)
3625 while (isspace(*p
)) p
++;
3628 if (*p
== '\"' || *p
== '\'')
3640 if ((q
- buf
) < buf_size
- 1)
3645 if (quote
&& *p
== quote
)
3650 /* add a codec and set the default parameters */
3651 static void add_codec(FFStream
*stream
, AVCodecContext
*av
)
3655 /* compute default parameters */
3656 switch(av
->codec_type
) {
3657 case CODEC_TYPE_AUDIO
:
3658 if (av
->bit_rate
== 0)
3659 av
->bit_rate
= 64000;
3660 if (av
->sample_rate
== 0)
3661 av
->sample_rate
= 22050;
3662 if (av
->channels
== 0)
3665 case CODEC_TYPE_VIDEO
:
3666 if (av
->bit_rate
== 0)
3667 av
->bit_rate
= 64000;
3668 if (av
->time_base
.num
== 0){
3669 av
->time_base
.den
= 5;
3670 av
->time_base
.num
= 1;
3672 if (av
->width
== 0 || av
->height
== 0) {
3676 /* Bitrate tolerance is less for streaming */
3677 if (av
->bit_rate_tolerance
== 0)
3678 av
->bit_rate_tolerance
= av
->bit_rate
/ 4;
3683 if (av
->max_qdiff
== 0)
3685 av
->qcompress
= 0.5;
3688 if (!av
->nsse_weight
)
3689 av
->nsse_weight
= 8;
3691 av
->frame_skip_cmp
= FF_CMP_DCTMAX
;
3692 av
->me_method
= ME_EPZS
;
3693 av
->rc_buffer_aggressivity
= 1.0;
3696 av
->rc_eq
= "tex^qComp";
3697 if (!av
->i_quant_factor
)
3698 av
->i_quant_factor
= -0.8;
3699 if (!av
->b_quant_factor
)
3700 av
->b_quant_factor
= 1.25;
3701 if (!av
->b_quant_offset
)
3702 av
->b_quant_offset
= 1.25;
3703 if (!av
->rc_max_rate
)
3704 av
->rc_max_rate
= av
->bit_rate
* 2;
3706 if (av
->rc_max_rate
&& !av
->rc_buffer_size
) {
3707 av
->rc_buffer_size
= av
->rc_max_rate
;
3716 st
= av_mallocz(sizeof(AVStream
));
3719 st
->codec
= avcodec_alloc_context();
3720 stream
->streams
[stream
->nb_streams
++] = st
;
3721 memcpy(st
->codec
, av
, sizeof(AVCodecContext
));
3724 static int opt_audio_codec(const char *arg
)
3730 if (!strcmp(p
->name
, arg
) && p
->type
== CODEC_TYPE_AUDIO
)
3735 return CODEC_ID_NONE
;
3741 static int opt_video_codec(const char *arg
)
3747 if (!strcmp(p
->name
, arg
) && p
->type
== CODEC_TYPE_VIDEO
)
3752 return CODEC_ID_NONE
;
3758 /* simplistic plugin support */
3761 static void load_module(const char *filename
)
3764 void (*init_func
)(void);
3765 dll
= dlopen(filename
, RTLD_NOW
);
3767 fprintf(stderr
, "Could not load module '%s' - %s\n",
3768 filename
, dlerror());
3772 init_func
= dlsym(dll
, "ffserver_module_init");
3775 "%s: init function 'ffserver_module_init()' not found\n",
3784 static int parse_ffconfig(const char *filename
)
3791 int val
, errors
, line_num
;
3792 FFStream
**last_stream
, *stream
, *redirect
;
3793 FFStream
**last_feed
, *feed
;
3794 AVCodecContext audio_enc
, video_enc
;
3795 int audio_id
, video_id
;
3797 f
= fopen(filename
, "r");
3805 first_stream
= NULL
;
3806 last_stream
= &first_stream
;
3808 last_feed
= &first_feed
;
3812 audio_id
= CODEC_ID_NONE
;
3813 video_id
= CODEC_ID_NONE
;
3815 if (fgets(line
, sizeof(line
), f
) == NULL
)
3821 if (*p
== '\0' || *p
== '#')
3824 get_arg(cmd
, sizeof(cmd
), &p
);
3826 if (!strcasecmp(cmd
, "Port")) {
3827 get_arg(arg
, sizeof(arg
), &p
);
3829 if (val
< 1 || val
> 65536) {
3830 fprintf(stderr
, "%s:%d: Invalid port: %s\n",
3831 filename
, line_num
, arg
);
3834 my_http_addr
.sin_port
= htons(val
);
3835 } else if (!strcasecmp(cmd
, "BindAddress")) {
3836 get_arg(arg
, sizeof(arg
), &p
);
3837 if (resolve_host(&my_http_addr
.sin_addr
, arg
) != 0) {
3838 fprintf(stderr
, "%s:%d: Invalid host/IP address: %s\n",
3839 filename
, line_num
, arg
);
3842 } else if (!strcasecmp(cmd
, "NoDaemon")) {
3843 ffserver_daemon
= 0;
3844 } else if (!strcasecmp(cmd
, "RTSPPort")) {
3845 get_arg(arg
, sizeof(arg
), &p
);
3847 if (val
< 1 || val
> 65536) {
3848 fprintf(stderr
, "%s:%d: Invalid port: %s\n",
3849 filename
, line_num
, arg
);
3852 my_rtsp_addr
.sin_port
= htons(atoi(arg
));
3853 } else if (!strcasecmp(cmd
, "RTSPBindAddress")) {
3854 get_arg(arg
, sizeof(arg
), &p
);
3855 if (resolve_host(&my_rtsp_addr
.sin_addr
, arg
) != 0) {
3856 fprintf(stderr
, "%s:%d: Invalid host/IP address: %s\n",
3857 filename
, line_num
, arg
);
3860 } else if (!strcasecmp(cmd
, "MaxClients")) {
3861 get_arg(arg
, sizeof(arg
), &p
);
3863 if (val
< 1 || val
> HTTP_MAX_CONNECTIONS
) {
3864 fprintf(stderr
, "%s:%d: Invalid MaxClients: %s\n",
3865 filename
, line_num
, arg
);
3868 nb_max_connections
= val
;
3870 } else if (!strcasecmp(cmd
, "MaxBandwidth")) {
3871 get_arg(arg
, sizeof(arg
), &p
);
3873 if (val
< 10 || val
> 100000) {
3874 fprintf(stderr
, "%s:%d: Invalid MaxBandwidth: %s\n",
3875 filename
, line_num
, arg
);
3878 max_bandwidth
= val
;
3880 } else if (!strcasecmp(cmd
, "CustomLog")) {
3881 get_arg(logfilename
, sizeof(logfilename
), &p
);
3882 } else if (!strcasecmp(cmd
, "<Feed")) {
3883 /*********************************************/
3884 /* Feed related options */
3886 if (stream
|| feed
) {
3887 fprintf(stderr
, "%s:%d: Already in a tag\n",
3888 filename
, line_num
);
3890 feed
= av_mallocz(sizeof(FFStream
));
3891 /* add in stream list */
3892 *last_stream
= feed
;
3893 last_stream
= &feed
->next
;
3894 /* add in feed list */
3896 last_feed
= &feed
->next_feed
;
3898 get_arg(feed
->filename
, sizeof(feed
->filename
), &p
);
3899 q
= strrchr(feed
->filename
, '>');
3902 feed
->fmt
= guess_format("ffm", NULL
, NULL
);
3903 /* defaut feed file */
3904 snprintf(feed
->feed_filename
, sizeof(feed
->feed_filename
),
3905 "/tmp/%s.ffm", feed
->filename
);
3906 feed
->feed_max_size
= 5 * 1024 * 1024;
3908 feed
->feed
= feed
; /* self feeding :-) */
3910 } else if (!strcasecmp(cmd
, "Launch")) {
3914 feed
->child_argv
= (char **) av_mallocz(64 * sizeof(char *));
3916 for (i
= 0; i
< 62; i
++) {
3917 get_arg(arg
, sizeof(arg
), &p
);
3921 feed
->child_argv
[i
] = av_strdup(arg
);
3924 feed
->child_argv
[i
] = av_malloc(30 + strlen(feed
->filename
));
3926 snprintf(feed
->child_argv
[i
], 30+strlen(feed
->filename
),
3928 (my_http_addr
.sin_addr
.s_addr
== INADDR_ANY
) ? "127.0.0.1" :
3929 inet_ntoa(my_http_addr
.sin_addr
),
3930 ntohs(my_http_addr
.sin_port
), feed
->filename
);
3935 fprintf(stdout
, "Launch commandline: ");
3936 for (j
= 0; j
<= i
; j
++)
3937 fprintf(stdout
, "%s ", feed
->child_argv
[j
]);
3938 fprintf(stdout
, "\n");
3941 } else if (!strcasecmp(cmd
, "ReadOnlyFile")) {
3943 get_arg(feed
->feed_filename
, sizeof(feed
->feed_filename
), &p
);
3945 } else if (stream
) {
3946 get_arg(stream
->feed_filename
, sizeof(stream
->feed_filename
), &p
);
3948 } else if (!strcasecmp(cmd
, "File")) {
3950 get_arg(feed
->feed_filename
, sizeof(feed
->feed_filename
), &p
);
3951 } else if (stream
) {
3952 get_arg(stream
->feed_filename
, sizeof(stream
->feed_filename
), &p
);
3954 } else if (!strcasecmp(cmd
, "FileMaxSize")) {
3959 get_arg(arg
, sizeof(arg
), &p
);
3961 fsize
= strtod(p1
, (char **)&p1
);
3962 switch(toupper(*p1
)) {
3967 fsize
*= 1024 * 1024;
3970 fsize
*= 1024 * 1024 * 1024;
3973 feed
->feed_max_size
= (int64_t)fsize
;
3975 } else if (!strcasecmp(cmd
, "</Feed>")) {
3977 fprintf(stderr
, "%s:%d: No corresponding <Feed> for </Feed>\n",
3978 filename
, line_num
);
3982 } else if (!strcasecmp(cmd
, "<Stream")) {
3983 /*********************************************/
3984 /* Stream related options */
3986 if (stream
|| feed
) {
3987 fprintf(stderr
, "%s:%d: Already in a tag\n",
3988 filename
, line_num
);
3990 stream
= av_mallocz(sizeof(FFStream
));
3991 *last_stream
= stream
;
3992 last_stream
= &stream
->next
;
3994 get_arg(stream
->filename
, sizeof(stream
->filename
), &p
);
3995 q
= strrchr(stream
->filename
, '>');
3998 stream
->fmt
= guess_stream_format(NULL
, stream
->filename
, NULL
);
3999 memset(&audio_enc
, 0, sizeof(AVCodecContext
));
4000 memset(&video_enc
, 0, sizeof(AVCodecContext
));
4001 audio_id
= CODEC_ID_NONE
;
4002 video_id
= CODEC_ID_NONE
;
4004 audio_id
= stream
->fmt
->audio_codec
;
4005 video_id
= stream
->fmt
->video_codec
;
4008 } else if (!strcasecmp(cmd
, "Feed")) {
4009 get_arg(arg
, sizeof(arg
), &p
);
4014 while (sfeed
!= NULL
) {
4015 if (!strcmp(sfeed
->filename
, arg
))
4017 sfeed
= sfeed
->next_feed
;
4020 fprintf(stderr
, "%s:%d: feed '%s' not defined\n",
4021 filename
, line_num
, arg
);
4023 stream
->feed
= sfeed
;
4026 } else if (!strcasecmp(cmd
, "Format")) {
4027 get_arg(arg
, sizeof(arg
), &p
);
4028 if (!strcmp(arg
, "status")) {
4029 stream
->stream_type
= STREAM_TYPE_STATUS
;
4032 stream
->stream_type
= STREAM_TYPE_LIVE
;
4033 /* jpeg cannot be used here, so use single frame jpeg */
4034 if (!strcmp(arg
, "jpeg"))
4035 strcpy(arg
, "mjpeg");
4036 stream
->fmt
= guess_stream_format(arg
, NULL
, NULL
);
4038 fprintf(stderr
, "%s:%d: Unknown Format: %s\n",
4039 filename
, line_num
, arg
);
4044 audio_id
= stream
->fmt
->audio_codec
;
4045 video_id
= stream
->fmt
->video_codec
;
4047 } else if (!strcasecmp(cmd
, "InputFormat")) {
4048 get_arg(arg
, sizeof(arg
), &p
);
4049 stream
->ifmt
= av_find_input_format(arg
);
4050 if (!stream
->ifmt
) {
4051 fprintf(stderr
, "%s:%d: Unknown input format: %s\n",
4052 filename
, line_num
, arg
);
4054 } else if (!strcasecmp(cmd
, "FaviconURL")) {
4055 if (stream
&& stream
->stream_type
== STREAM_TYPE_STATUS
) {
4056 get_arg(stream
->feed_filename
, sizeof(stream
->feed_filename
), &p
);
4058 fprintf(stderr
, "%s:%d: FaviconURL only permitted for status streams\n",
4059 filename
, line_num
);
4062 } else if (!strcasecmp(cmd
, "Author")) {
4064 get_arg(stream
->author
, sizeof(stream
->author
), &p
);
4066 } else if (!strcasecmp(cmd
, "Comment")) {
4068 get_arg(stream
->comment
, sizeof(stream
->comment
), &p
);
4070 } else if (!strcasecmp(cmd
, "Copyright")) {
4072 get_arg(stream
->copyright
, sizeof(stream
->copyright
), &p
);
4074 } else if (!strcasecmp(cmd
, "Title")) {
4076 get_arg(stream
->title
, sizeof(stream
->title
), &p
);
4078 } else if (!strcasecmp(cmd
, "Preroll")) {
4079 get_arg(arg
, sizeof(arg
), &p
);
4081 stream
->prebuffer
= atof(arg
) * 1000;
4083 } else if (!strcasecmp(cmd
, "StartSendOnKey")) {
4085 stream
->send_on_key
= 1;
4087 } else if (!strcasecmp(cmd
, "AudioCodec")) {
4088 get_arg(arg
, sizeof(arg
), &p
);
4089 audio_id
= opt_audio_codec(arg
);
4090 if (audio_id
== CODEC_ID_NONE
) {
4091 fprintf(stderr
, "%s:%d: Unknown AudioCodec: %s\n",
4092 filename
, line_num
, arg
);
4095 } else if (!strcasecmp(cmd
, "VideoCodec")) {
4096 get_arg(arg
, sizeof(arg
), &p
);
4097 video_id
= opt_video_codec(arg
);
4098 if (video_id
== CODEC_ID_NONE
) {
4099 fprintf(stderr
, "%s:%d: Unknown VideoCodec: %s\n",
4100 filename
, line_num
, arg
);
4103 } else if (!strcasecmp(cmd
, "MaxTime")) {
4104 get_arg(arg
, sizeof(arg
), &p
);
4106 stream
->max_time
= atof(arg
) * 1000;
4108 } else if (!strcasecmp(cmd
, "AudioBitRate")) {
4109 get_arg(arg
, sizeof(arg
), &p
);
4111 audio_enc
.bit_rate
= atoi(arg
) * 1000;
4113 } else if (!strcasecmp(cmd
, "AudioChannels")) {
4114 get_arg(arg
, sizeof(arg
), &p
);
4116 audio_enc
.channels
= atoi(arg
);
4118 } else if (!strcasecmp(cmd
, "AudioSampleRate")) {
4119 get_arg(arg
, sizeof(arg
), &p
);
4121 audio_enc
.sample_rate
= atoi(arg
);
4123 } else if (!strcasecmp(cmd
, "AudioQuality")) {
4124 get_arg(arg
, sizeof(arg
), &p
);
4126 // audio_enc.quality = atof(arg) * 1000;
4128 } else if (!strcasecmp(cmd
, "VideoBitRateRange")) {
4130 int minrate
, maxrate
;
4132 get_arg(arg
, sizeof(arg
), &p
);
4134 if (sscanf(arg
, "%d-%d", &minrate
, &maxrate
) == 2) {
4135 video_enc
.rc_min_rate
= minrate
* 1000;
4136 video_enc
.rc_max_rate
= maxrate
* 1000;
4138 fprintf(stderr
, "%s:%d: Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n",
4139 filename
, line_num
, arg
);
4143 } else if (!strcasecmp(cmd
, "Debug")) {
4145 get_arg(arg
, sizeof(arg
), &p
);
4146 video_enc
.debug
= strtol(arg
,0,0);
4148 } else if (!strcasecmp(cmd
, "Strict")) {
4150 get_arg(arg
, sizeof(arg
), &p
);
4151 video_enc
.strict_std_compliance
= atoi(arg
);
4153 } else if (!strcasecmp(cmd
, "VideoBufferSize")) {
4155 get_arg(arg
, sizeof(arg
), &p
);
4156 video_enc
.rc_buffer_size
= atoi(arg
) * 8*1024;
4158 } else if (!strcasecmp(cmd
, "VideoBitRateTolerance")) {
4160 get_arg(arg
, sizeof(arg
), &p
);
4161 video_enc
.bit_rate_tolerance
= atoi(arg
) * 1000;
4163 } else if (!strcasecmp(cmd
, "VideoBitRate")) {
4164 get_arg(arg
, sizeof(arg
), &p
);
4166 video_enc
.bit_rate
= atoi(arg
) * 1000;
4168 } else if (!strcasecmp(cmd
, "VideoSize")) {
4169 get_arg(arg
, sizeof(arg
), &p
);
4171 av_parse_video_frame_size(&video_enc
.width
, &video_enc
.height
, arg
);
4172 if ((video_enc
.width
% 16) != 0 ||
4173 (video_enc
.height
% 16) != 0) {
4174 fprintf(stderr
, "%s:%d: Image size must be a multiple of 16\n",
4175 filename
, line_num
);
4179 } else if (!strcasecmp(cmd
, "VideoFrameRate")) {
4180 get_arg(arg
, sizeof(arg
), &p
);
4182 video_enc
.time_base
.num
= DEFAULT_FRAME_RATE_BASE
;
4183 video_enc
.time_base
.den
= (int)(strtod(arg
, NULL
) * video_enc
.time_base
.num
);
4185 } else if (!strcasecmp(cmd
, "VideoGopSize")) {
4186 get_arg(arg
, sizeof(arg
), &p
);
4188 video_enc
.gop_size
= atoi(arg
);
4190 } else if (!strcasecmp(cmd
, "VideoIntraOnly")) {
4192 video_enc
.gop_size
= 1;
4194 } else if (!strcasecmp(cmd
, "VideoHighQuality")) {
4196 video_enc
.mb_decision
= FF_MB_DECISION_BITS
;
4198 } else if (!strcasecmp(cmd
, "Video4MotionVector")) {
4200 video_enc
.mb_decision
= FF_MB_DECISION_BITS
; //FIXME remove
4201 video_enc
.flags
|= CODEC_FLAG_4MV
;
4203 } else if (!strcasecmp(cmd
, "VideoTag")) {
4204 get_arg(arg
, sizeof(arg
), &p
);
4205 if ((strlen(arg
) == 4) && stream
) {
4206 video_enc
.codec_tag
= ff_get_fourcc(arg
);
4208 } else if (!strcasecmp(cmd
, "BitExact")) {
4210 video_enc
.flags
|= CODEC_FLAG_BITEXACT
;
4212 } else if (!strcasecmp(cmd
, "DctFastint")) {
4214 video_enc
.dct_algo
= FF_DCT_FASTINT
;
4216 } else if (!strcasecmp(cmd
, "IdctSimple")) {
4218 video_enc
.idct_algo
= FF_IDCT_SIMPLE
;
4220 } else if (!strcasecmp(cmd
, "Qscale")) {
4221 get_arg(arg
, sizeof(arg
), &p
);
4223 video_enc
.flags
|= CODEC_FLAG_QSCALE
;
4224 video_enc
.global_quality
= FF_QP2LAMBDA
* atoi(arg
);
4226 } else if (!strcasecmp(cmd
, "VideoQDiff")) {
4227 get_arg(arg
, sizeof(arg
), &p
);
4229 video_enc
.max_qdiff
= atoi(arg
);
4230 if (video_enc
.max_qdiff
< 1 || video_enc
.max_qdiff
> 31) {
4231 fprintf(stderr
, "%s:%d: VideoQDiff out of range\n",
4232 filename
, line_num
);
4236 } else if (!strcasecmp(cmd
, "VideoQMax")) {
4237 get_arg(arg
, sizeof(arg
), &p
);
4239 video_enc
.qmax
= atoi(arg
);
4240 if (video_enc
.qmax
< 1 || video_enc
.qmax
> 31) {
4241 fprintf(stderr
, "%s:%d: VideoQMax out of range\n",
4242 filename
, line_num
);
4246 } else if (!strcasecmp(cmd
, "VideoQMin")) {
4247 get_arg(arg
, sizeof(arg
), &p
);
4249 video_enc
.qmin
= atoi(arg
);
4250 if (video_enc
.qmin
< 1 || video_enc
.qmin
> 31) {
4251 fprintf(stderr
, "%s:%d: VideoQMin out of range\n",
4252 filename
, line_num
);
4256 } else if (!strcasecmp(cmd
, "LumaElim")) {
4257 get_arg(arg
, sizeof(arg
), &p
);
4259 video_enc
.luma_elim_threshold
= atoi(arg
);
4261 } else if (!strcasecmp(cmd
, "ChromaElim")) {
4262 get_arg(arg
, sizeof(arg
), &p
);
4264 video_enc
.chroma_elim_threshold
= atoi(arg
);
4266 } else if (!strcasecmp(cmd
, "LumiMask")) {
4267 get_arg(arg
, sizeof(arg
), &p
);
4269 video_enc
.lumi_masking
= atof(arg
);
4271 } else if (!strcasecmp(cmd
, "DarkMask")) {
4272 get_arg(arg
, sizeof(arg
), &p
);
4274 video_enc
.dark_masking
= atof(arg
);
4276 } else if (!strcasecmp(cmd
, "NoVideo")) {
4277 video_id
= CODEC_ID_NONE
;
4278 } else if (!strcasecmp(cmd
, "NoAudio")) {
4279 audio_id
= CODEC_ID_NONE
;
4280 } else if (!strcasecmp(cmd
, "ACL")) {
4283 get_arg(arg
, sizeof(arg
), &p
);
4284 if (strcasecmp(arg
, "allow") == 0) {
4285 acl
.action
= IP_ALLOW
;
4286 } else if (strcasecmp(arg
, "deny") == 0) {
4287 acl
.action
= IP_DENY
;
4289 fprintf(stderr
, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
4290 filename
, line_num
, arg
);
4294 get_arg(arg
, sizeof(arg
), &p
);
4296 if (resolve_host(&acl
.first
, arg
) != 0) {
4297 fprintf(stderr
, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4298 filename
, line_num
, arg
);
4301 acl
.last
= acl
.first
;
4304 get_arg(arg
, sizeof(arg
), &p
);
4307 if (resolve_host(&acl
.last
, arg
) != 0) {
4308 fprintf(stderr
, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4309 filename
, line_num
, arg
);
4315 IPAddressACL
*nacl
= (IPAddressACL
*) av_mallocz(sizeof(*nacl
));
4316 IPAddressACL
**naclp
= 0;
4322 naclp
= &stream
->acl
;
4326 fprintf(stderr
, "%s:%d: ACL found not in <stream> or <feed>\n",
4327 filename
, line_num
);
4333 naclp
= &(*naclp
)->next
;
4338 } else if (!strcasecmp(cmd
, "RTSPOption")) {
4339 get_arg(arg
, sizeof(arg
), &p
);
4341 av_freep(&stream
->rtsp_option
);
4342 stream
->rtsp_option
= av_strdup(arg
);
4344 } else if (!strcasecmp(cmd
, "MulticastAddress")) {
4345 get_arg(arg
, sizeof(arg
), &p
);
4347 if (resolve_host(&stream
->multicast_ip
, arg
) != 0) {
4348 fprintf(stderr
, "%s:%d: Invalid host/IP address: %s\n",
4349 filename
, line_num
, arg
);
4352 stream
->is_multicast
= 1;
4353 stream
->loop
= 1; /* default is looping */
4355 } else if (!strcasecmp(cmd
, "MulticastPort")) {
4356 get_arg(arg
, sizeof(arg
), &p
);
4358 stream
->multicast_port
= atoi(arg
);
4360 } else if (!strcasecmp(cmd
, "MulticastTTL")) {
4361 get_arg(arg
, sizeof(arg
), &p
);
4363 stream
->multicast_ttl
= atoi(arg
);
4365 } else if (!strcasecmp(cmd
, "NoLoop")) {
4369 } else if (!strcasecmp(cmd
, "</Stream>")) {
4371 fprintf(stderr
, "%s:%d: No corresponding <Stream> for </Stream>\n",
4372 filename
, line_num
);
4375 if (stream
->feed
&& stream
->fmt
&& strcmp(stream
->fmt
->name
, "ffm") != 0) {
4376 if (audio_id
!= CODEC_ID_NONE
) {
4377 audio_enc
.codec_type
= CODEC_TYPE_AUDIO
;
4378 audio_enc
.codec_id
= audio_id
;
4379 add_codec(stream
, &audio_enc
);
4381 if (video_id
!= CODEC_ID_NONE
) {
4382 video_enc
.codec_type
= CODEC_TYPE_VIDEO
;
4383 video_enc
.codec_id
= video_id
;
4384 add_codec(stream
, &video_enc
);
4388 } else if (!strcasecmp(cmd
, "<Redirect")) {
4389 /*********************************************/
4391 if (stream
|| feed
|| redirect
) {
4392 fprintf(stderr
, "%s:%d: Already in a tag\n",
4393 filename
, line_num
);
4396 redirect
= av_mallocz(sizeof(FFStream
));
4397 *last_stream
= redirect
;
4398 last_stream
= &redirect
->next
;
4400 get_arg(redirect
->filename
, sizeof(redirect
->filename
), &p
);
4401 q
= strrchr(redirect
->filename
, '>');
4404 redirect
->stream_type
= STREAM_TYPE_REDIRECT
;
4406 } else if (!strcasecmp(cmd
, "URL")) {
4408 get_arg(redirect
->feed_filename
, sizeof(redirect
->feed_filename
), &p
);
4410 } else if (!strcasecmp(cmd
, "</Redirect>")) {
4412 fprintf(stderr
, "%s:%d: No corresponding <Redirect> for </Redirect>\n",
4413 filename
, line_num
);
4416 if (!redirect
->feed_filename
[0]) {
4417 fprintf(stderr
, "%s:%d: No URL found for <Redirect>\n",
4418 filename
, line_num
);
4422 } else if (!strcasecmp(cmd
, "LoadModule")) {
4423 get_arg(arg
, sizeof(arg
), &p
);
4427 fprintf(stderr
, "%s:%d: Module support not compiled into this version: '%s'\n",
4428 filename
, line_num
, arg
);
4432 fprintf(stderr
, "%s:%d: Incorrect keyword: '%s'\n",
4433 filename
, line_num
, cmd
);
4445 static void show_banner(void)
4447 printf("ffserver version " FFMPEG_VERSION
", Copyright (c) 2000-2006 Fabrice Bellard, et al.\n");
4450 static void show_help(void)
4453 printf("usage: ffserver [-L] [-h] [-f configfile]\n"
4454 "Hyper fast multi format Audio/Video streaming server\n"
4456 "-L : print the LICENSE\n"
4458 "-f configfile : use configfile instead of /etc/ffserver.conf\n"
4462 static void show_license(void)
4466 "FFmpeg is free software; you can redistribute it and/or\n"
4467 "modify it under the terms of the GNU Lesser General Public\n"
4468 "License as published by the Free Software Foundation; either\n"
4469 "version 2.1 of the License, or (at your option) any later version.\n"
4471 "FFmpeg is distributed in the hope that it will be useful,\n"
4472 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
4473 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
4474 "Lesser General Public License for more details.\n"
4476 "You should have received a copy of the GNU Lesser General Public\n"
4477 "License along with FFmpeg; if not, write to the Free Software\n"
4478 "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
4482 static void handle_child_exit(int sig
)
4487 while ((pid
= waitpid(-1, &status
, WNOHANG
)) > 0) {
4490 for (feed
= first_feed
; feed
; feed
= feed
->next
) {
4491 if (feed
->pid
== pid
) {
4492 int uptime
= time(0) - feed
->pid_start
;
4495 fprintf(stderr
, "%s: Pid %d exited with status %d after %d seconds\n", feed
->filename
, pid
, status
, uptime
);
4498 /* Turn off any more restarts */
4499 feed
->child_argv
= 0;
4505 need_to_start_children
= 1;
4508 int main(int argc
, char **argv
)
4510 const char *config_filename
;
4512 struct sigaction sigact
;
4516 config_filename
= "/etc/ffserver.conf";
4518 my_program_name
= argv
[0];
4519 my_program_dir
= getcwd(0, 0);
4520 ffserver_daemon
= 1;
4523 c
= getopt(argc
, argv
, "ndLh?f:");
4539 ffserver_daemon
= 0;
4542 config_filename
= optarg
;
4549 putenv("http_proxy"); /* Kill the http_proxy */
4551 av_init_random(av_gettime() + (getpid() << 16), &random_state
);
4553 /* address on which the server will handle HTTP connections */
4554 my_http_addr
.sin_family
= AF_INET
;
4555 my_http_addr
.sin_port
= htons (8080);
4556 my_http_addr
.sin_addr
.s_addr
= htonl (INADDR_ANY
);
4558 /* address on which the server will handle RTSP connections */
4559 my_rtsp_addr
.sin_family
= AF_INET
;
4560 my_rtsp_addr
.sin_port
= htons (5454);
4561 my_rtsp_addr
.sin_addr
.s_addr
= htonl (INADDR_ANY
);
4563 nb_max_connections
= 5;
4564 max_bandwidth
= 1000;
4565 first_stream
= NULL
;
4566 logfilename
[0] = '\0';
4568 memset(&sigact
, 0, sizeof(sigact
));
4569 sigact
.sa_handler
= handle_child_exit
;
4570 sigact
.sa_flags
= SA_NOCLDSTOP
| SA_RESTART
;
4571 sigaction(SIGCHLD
, &sigact
, 0);
4573 if (parse_ffconfig(config_filename
) < 0) {
4574 fprintf(stderr
, "Incorrect config file - exiting.\n");
4578 build_file_streams();
4580 build_feed_streams();
4582 compute_bandwidth();
4584 /* put the process in background and detach it from its TTY */
4585 if (ffserver_daemon
) {
4592 } else if (pid
> 0) {
4600 open("/dev/null", O_RDWR
);
4601 if (strcmp(logfilename
, "-") != 0) {
4611 signal(SIGPIPE
, SIG_IGN
);
4613 /* open log file if needed */
4614 if (logfilename
[0] != '\0') {
4615 if (!strcmp(logfilename
, "-"))
4618 logfile
= fopen(logfilename
, "w");
4621 if (http_server() < 0) {
4622 fprintf(stderr
, "Could not start server\n");