2 * Multiple format streaming server
3 * Copyright (c) 2000, 2001, 2002 Fabrice Bellard
5 * This file is part of FFmpeg.
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 #define _XOPEN_SOURCE 600
25 #ifndef HAVE_CLOSESOCKET
26 #define closesocket close
31 #include "libavutil/random.h"
32 #include "libavutil/avstring.h"
33 #include "libavformat/avformat.h"
34 #include "libavformat/network.h"
35 #include "libavformat/os_support.h"
36 #include "libavformat/rtp.h"
37 #include "libavformat/rtsp.h"
38 #include "libavcodec/opt.h"
42 #include <sys/ioctl.h>
48 #undef time //needed because HAVE_AV_CONFIG_H is defined on top
60 const char program_name
[] = "FFserver";
61 const int program_birth_year
= 2000;
63 static const OptionDef options
[];
66 HTTPSTATE_WAIT_REQUEST
,
67 HTTPSTATE_SEND_HEADER
,
68 HTTPSTATE_SEND_DATA_HEADER
,
69 HTTPSTATE_SEND_DATA
, /* sending TCP or UDP data */
70 HTTPSTATE_SEND_DATA_TRAILER
,
71 HTTPSTATE_RECEIVE_DATA
,
72 HTTPSTATE_WAIT_FEED
, /* wait for data from the feed */
75 RTSPSTATE_WAIT_REQUEST
,
77 RTSPSTATE_SEND_PACKET
,
80 static const char *http_state
[] = {
96 #define IOBUFFER_INIT_SIZE 8192
98 /* timeouts are in ms */
99 #define HTTP_REQUEST_TIMEOUT (15 * 1000)
100 #define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
102 #define SYNC_TIMEOUT (10 * 1000)
105 int64_t count1
, count2
;
106 int64_t time1
, time2
;
109 /* context associated with one connection */
110 typedef struct HTTPContext
{
111 enum HTTPState state
;
112 int fd
; /* socket file descriptor */
113 struct sockaddr_in from_addr
; /* origin */
114 struct pollfd
*poll_entry
; /* used when polling */
116 uint8_t *buffer_ptr
, *buffer_end
;
119 struct HTTPContext
*next
;
120 int got_key_frame
; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
124 /* input format handling */
125 AVFormatContext
*fmt_in
;
126 int64_t start_time
; /* In milliseconds - this wraps fairly often */
127 int64_t first_pts
; /* initial pts value */
128 int64_t cur_pts
; /* current pts value from the stream in us */
129 int64_t cur_frame_duration
; /* duration of the current frame in us */
130 int cur_frame_bytes
; /* output frame size, needed to compute
131 the time at which we send each
133 int pts_stream_index
; /* stream we choose as clock reference */
134 int64_t cur_clock
; /* current clock reference value in us */
135 /* output format handling */
136 struct FFStream
*stream
;
137 /* -1 is invalid stream */
138 int feed_streams
[MAX_STREAMS
]; /* index of streams in the feed */
139 int switch_feed_streams
[MAX_STREAMS
]; /* index of streams in the feed */
141 AVFormatContext fmt_ctx
; /* instance of FFStream for one user */
142 int last_packet_sent
; /* true if last data packet was sent */
144 DataRateData datarate
;
151 int is_packetized
; /* if true, the stream is packetized */
152 int packet_stream_index
; /* current stream for output in state machine */
154 /* RTSP state specific */
155 uint8_t *pb_buffer
; /* XXX: use that in all the code */
157 int seq
; /* RTSP sequence number */
159 /* RTP state specific */
160 enum RTSPLowerTransport rtp_protocol
;
161 char session_id
[32]; /* session id */
162 AVFormatContext
*rtp_ctx
[MAX_STREAMS
];
164 /* RTP/UDP specific */
165 URLContext
*rtp_handles
[MAX_STREAMS
];
167 /* RTP/TCP specific */
168 struct HTTPContext
*rtsp_c
;
169 uint8_t *packet_buffer
, *packet_buffer_ptr
, *packet_buffer_end
;
172 /* each generated stream is described here */
176 STREAM_TYPE_REDIRECT
,
179 enum IPAddressAction
{
184 typedef struct IPAddressACL
{
185 struct IPAddressACL
*next
;
186 enum IPAddressAction action
;
187 /* These are in host order */
188 struct in_addr first
;
192 /* description of each stream of the ffserver.conf file */
193 typedef struct FFStream
{
194 enum StreamType stream_type
;
195 char filename
[1024]; /* stream filename */
196 struct FFStream
*feed
; /* feed we are using (can be null if
198 AVFormatParameters
*ap_in
; /* input parameters */
199 AVInputFormat
*ifmt
; /* if non NULL, force input format */
203 int prebuffer
; /* Number of millseconds early to start */
204 int64_t max_time
; /* Number of milliseconds to run */
206 AVStream
*streams
[MAX_STREAMS
];
207 int feed_streams
[MAX_STREAMS
]; /* index of streams in the feed */
208 char feed_filename
[1024]; /* file name of the feed storage, or
209 input file name for a stream */
214 pid_t pid
; /* Of ffmpeg process */
215 time_t pid_start
; /* Of ffmpeg process */
217 struct FFStream
*next
;
218 unsigned bandwidth
; /* bandwidth, in kbits/s */
221 /* multicast specific */
223 struct in_addr multicast_ip
;
224 int multicast_port
; /* first port used for multicast */
226 int loop
; /* if true, send the stream in loops (only meaningful if file) */
229 int feed_opened
; /* true if someone is writing to the feed */
230 int is_feed
; /* true if it is a feed */
231 int readonly
; /* True if writing is prohibited to the file */
233 int64_t bytes_served
;
234 int64_t feed_max_size
; /* maximum storage size, zero means unlimited */
235 int64_t feed_write_index
; /* current write position in feed (it wraps around) */
236 int64_t feed_size
; /* current size of feed */
237 struct FFStream
*next_feed
;
240 typedef struct FeedData
{
241 long long data_count
;
242 float avg_frame_size
; /* frame size averaged over last frames with exponential mean */
245 static struct sockaddr_in my_http_addr
;
246 static struct sockaddr_in my_rtsp_addr
;
248 static char logfilename
[1024];
249 static HTTPContext
*first_http_ctx
;
250 static FFStream
*first_feed
; /* contains only feeds */
251 static FFStream
*first_stream
; /* contains all streams, including feeds */
253 static void new_connection(int server_fd
, int is_rtsp
);
254 static void close_connection(HTTPContext
*c
);
257 static int handle_connection(HTTPContext
*c
);
258 static int http_parse_request(HTTPContext
*c
);
259 static int http_send_data(HTTPContext
*c
);
260 static void compute_status(HTTPContext
*c
);
261 static int open_input_stream(HTTPContext
*c
, const char *info
);
262 static int http_start_receive_data(HTTPContext
*c
);
263 static int http_receive_data(HTTPContext
*c
);
266 static int rtsp_parse_request(HTTPContext
*c
);
267 static void rtsp_cmd_describe(HTTPContext
*c
, const char *url
);
268 static void rtsp_cmd_options(HTTPContext
*c
, const char *url
);
269 static void rtsp_cmd_setup(HTTPContext
*c
, const char *url
, RTSPHeader
*h
);
270 static void rtsp_cmd_play(HTTPContext
*c
, const char *url
, RTSPHeader
*h
);
271 static void rtsp_cmd_pause(HTTPContext
*c
, const char *url
, RTSPHeader
*h
);
272 static void rtsp_cmd_teardown(HTTPContext
*c
, const char *url
, RTSPHeader
*h
);
275 static int prepare_sdp_description(FFStream
*stream
, uint8_t **pbuffer
,
276 struct in_addr my_ip
);
279 static HTTPContext
*rtp_new_connection(struct sockaddr_in
*from_addr
,
280 FFStream
*stream
, const char *session_id
,
281 enum RTSPLowerTransport rtp_protocol
);
282 static int rtp_new_av_stream(HTTPContext
*c
,
283 int stream_index
, struct sockaddr_in
*dest_addr
,
284 HTTPContext
*rtsp_c
);
286 static const char *my_program_name
;
287 static const char *my_program_dir
;
289 static const char *config_filename
;
290 static int ffserver_debug
;
291 static int ffserver_daemon
;
292 static int no_launch
;
293 static int need_to_start_children
;
295 /* maximum number of simultaneous HTTP connections */
296 static unsigned int nb_max_http_connections
= 2000;
297 static unsigned int nb_max_connections
= 5;
298 static unsigned int nb_connections
;
300 static uint64_t max_bandwidth
= 1000;
301 static uint64_t current_bandwidth
;
303 static int64_t cur_time
; // Making this global saves on passing it around everywhere
305 static AVRandomState random_state
;
307 static FILE *logfile
= NULL
;
309 static char *ctime1(char *buf2
)
317 p
= buf2
+ strlen(p
) - 1;
323 static void http_vlog(const char *fmt
, va_list vargs
)
325 static int print_prefix
= 1;
330 fprintf(logfile
, "%s ", buf
);
332 print_prefix
= strstr(fmt
, "\n") != NULL
;
333 vfprintf(logfile
, fmt
, vargs
);
338 void __attribute__ ((format (printf
, 1, 2))) http_log(const char *fmt
, ...)
341 va_start(vargs
, fmt
);
342 http_vlog(fmt
, vargs
);
346 static void http_av_log(void *ptr
, int level
, const char *fmt
, va_list vargs
)
348 static int print_prefix
= 1;
349 AVClass
*avc
= ptr
? *(AVClass
**)ptr
: NULL
;
350 if (level
> av_log_level
)
352 if (print_prefix
&& avc
)
353 http_log("[%s @ %p]", avc
->item_name(ptr
), ptr
);
354 print_prefix
= strstr(fmt
, "\n") != NULL
;
355 http_vlog(fmt
, vargs
);
358 static void log_connection(HTTPContext
*c
)
363 http_log("%s - - [%s] \"%s %s\" %d %"PRId64
"\n",
364 inet_ntoa(c
->from_addr
.sin_addr
), c
->method
, c
->url
,
365 c
->protocol
, (c
->http_error
? c
->http_error
: 200), c
->data_count
);
368 static void update_datarate(DataRateData
*drd
, int64_t count
)
370 if (!drd
->time1
&& !drd
->count1
) {
371 drd
->time1
= drd
->time2
= cur_time
;
372 drd
->count1
= drd
->count2
= count
;
373 } else if (cur_time
- drd
->time2
> 5000) {
374 drd
->time1
= drd
->time2
;
375 drd
->count1
= drd
->count2
;
376 drd
->time2
= cur_time
;
381 /* In bytes per second */
382 static int compute_datarate(DataRateData
*drd
, int64_t count
)
384 if (cur_time
== drd
->time1
)
387 return ((count
- drd
->count1
) * 1000) / (cur_time
- drd
->time1
);
391 static void start_children(FFStream
*feed
)
396 for (; feed
; feed
= feed
->next
) {
397 if (feed
->child_argv
&& !feed
->pid
) {
398 feed
->pid_start
= time(0);
403 http_log("Unable to create children\n");
412 av_strlcpy(pathname
, my_program_name
, sizeof(pathname
));
414 slash
= strrchr(pathname
, '/');
419 strcpy(slash
, "ffmpeg");
421 http_log("Launch commandline: ");
422 http_log("%s ", pathname
);
423 for (i
= 1; feed
->child_argv
[i
] && feed
->child_argv
[i
][0]; i
++)
424 http_log("%s ", feed
->child_argv
[i
]);
427 for (i
= 3; i
< 256; i
++)
430 if (!ffserver_debug
) {
431 i
= open("/dev/null", O_RDWR
);
440 /* This is needed to make relative pathnames work */
441 chdir(my_program_dir
);
443 signal(SIGPIPE
, SIG_DFL
);
445 execvp(pathname
, feed
->child_argv
);
453 /* open a listening socket */
454 static int socket_open_listen(struct sockaddr_in
*my_addr
)
458 server_fd
= socket(AF_INET
,SOCK_STREAM
,0);
465 setsockopt(server_fd
, SOL_SOCKET
, SO_REUSEADDR
, &tmp
, sizeof(tmp
));
467 if (bind (server_fd
, (struct sockaddr
*) my_addr
, sizeof (*my_addr
)) < 0) {
469 snprintf(bindmsg
, sizeof(bindmsg
), "bind(port %d)", ntohs(my_addr
->sin_port
));
471 closesocket(server_fd
);
475 if (listen (server_fd
, 5) < 0) {
477 closesocket(server_fd
);
480 ff_socket_nonblock(server_fd
, 1);
485 /* start all multicast streams */
486 static void start_multicast(void)
491 struct sockaddr_in dest_addr
;
492 int default_port
, stream_index
;
495 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
496 if (stream
->is_multicast
) {
497 /* open the RTP connection */
498 snprintf(session_id
, sizeof(session_id
), "%08x%08x",
499 av_random(&random_state
), av_random(&random_state
));
501 /* choose a port if none given */
502 if (stream
->multicast_port
== 0) {
503 stream
->multicast_port
= default_port
;
507 dest_addr
.sin_family
= AF_INET
;
508 dest_addr
.sin_addr
= stream
->multicast_ip
;
509 dest_addr
.sin_port
= htons(stream
->multicast_port
);
511 rtp_c
= rtp_new_connection(&dest_addr
, stream
, session_id
,
512 RTSP_LOWER_TRANSPORT_UDP_MULTICAST
);
516 if (open_input_stream(rtp_c
, "") < 0) {
517 http_log("Could not open input stream for stream '%s'\n",
522 /* open each RTP stream */
523 for(stream_index
= 0; stream_index
< stream
->nb_streams
;
525 dest_addr
.sin_port
= htons(stream
->multicast_port
+
527 if (rtp_new_av_stream(rtp_c
, stream_index
, &dest_addr
, NULL
) < 0) {
528 http_log("Could not open output stream '%s/streamid=%d'\n",
529 stream
->filename
, stream_index
);
534 /* change state to send data */
535 rtp_c
->state
= HTTPSTATE_SEND_DATA
;
540 /* main loop of the http server */
541 static int http_server(void)
543 int server_fd
= 0, rtsp_server_fd
= 0;
544 int ret
, delay
, delay1
;
545 struct pollfd
*poll_table
, *poll_entry
;
546 HTTPContext
*c
, *c_next
;
548 if(!(poll_table
= av_mallocz((nb_max_http_connections
+ 2)*sizeof(*poll_table
)))) {
549 http_log("Impossible to allocate a poll table handling %d connections.\n", nb_max_http_connections
);
553 if (my_http_addr
.sin_port
) {
554 server_fd
= socket_open_listen(&my_http_addr
);
559 if (my_rtsp_addr
.sin_port
) {
560 rtsp_server_fd
= socket_open_listen(&my_rtsp_addr
);
561 if (rtsp_server_fd
< 0)
565 if (!rtsp_server_fd
&& !server_fd
) {
566 http_log("HTTP and RTSP disabled.\n");
570 http_log("FFserver started.\n");
572 start_children(first_feed
);
577 poll_entry
= poll_table
;
579 poll_entry
->fd
= server_fd
;
580 poll_entry
->events
= POLLIN
;
583 if (rtsp_server_fd
) {
584 poll_entry
->fd
= rtsp_server_fd
;
585 poll_entry
->events
= POLLIN
;
589 /* wait for events on each HTTP handle */
596 case HTTPSTATE_SEND_HEADER
:
597 case RTSPSTATE_SEND_REPLY
:
598 case RTSPSTATE_SEND_PACKET
:
599 c
->poll_entry
= poll_entry
;
601 poll_entry
->events
= POLLOUT
;
604 case HTTPSTATE_SEND_DATA_HEADER
:
605 case HTTPSTATE_SEND_DATA
:
606 case HTTPSTATE_SEND_DATA_TRAILER
:
607 if (!c
->is_packetized
) {
608 /* for TCP, we output as much as we can (may need to put a limit) */
609 c
->poll_entry
= poll_entry
;
611 poll_entry
->events
= POLLOUT
;
614 /* when ffserver is doing the timing, we work by
615 looking at which packet need to be sent every
617 delay1
= 10; /* one tick wait XXX: 10 ms assumed */
622 case HTTPSTATE_WAIT_REQUEST
:
623 case HTTPSTATE_RECEIVE_DATA
:
624 case HTTPSTATE_WAIT_FEED
:
625 case RTSPSTATE_WAIT_REQUEST
:
626 /* need to catch errors */
627 c
->poll_entry
= poll_entry
;
629 poll_entry
->events
= POLLIN
;/* Maybe this will work */
633 c
->poll_entry
= NULL
;
639 /* wait for an event on one connection. We poll at least every
640 second to handle timeouts */
642 ret
= poll(poll_table
, poll_entry
- poll_table
, delay
);
643 if (ret
< 0 && ff_neterrno() != FF_NETERROR(EAGAIN
) &&
644 ff_neterrno() != FF_NETERROR(EINTR
))
648 cur_time
= av_gettime() / 1000;
650 if (need_to_start_children
) {
651 need_to_start_children
= 0;
652 start_children(first_feed
);
655 /* now handle the events */
656 for(c
= first_http_ctx
; c
!= NULL
; c
= c_next
) {
658 if (handle_connection(c
) < 0) {
659 /* close and free the connection */
665 poll_entry
= poll_table
;
667 /* new HTTP connection request ? */
668 if (poll_entry
->revents
& POLLIN
)
669 new_connection(server_fd
, 0);
672 if (rtsp_server_fd
) {
673 /* new RTSP connection request ? */
674 if (poll_entry
->revents
& POLLIN
)
675 new_connection(rtsp_server_fd
, 1);
680 /* start waiting for a new HTTP/RTSP request */
681 static void start_wait_request(HTTPContext
*c
, int is_rtsp
)
683 c
->buffer_ptr
= c
->buffer
;
684 c
->buffer_end
= c
->buffer
+ c
->buffer_size
- 1; /* leave room for '\0' */
687 c
->timeout
= cur_time
+ RTSP_REQUEST_TIMEOUT
;
688 c
->state
= RTSPSTATE_WAIT_REQUEST
;
690 c
->timeout
= cur_time
+ HTTP_REQUEST_TIMEOUT
;
691 c
->state
= HTTPSTATE_WAIT_REQUEST
;
695 static void new_connection(int server_fd
, int is_rtsp
)
697 struct sockaddr_in from_addr
;
699 HTTPContext
*c
= NULL
;
701 len
= sizeof(from_addr
);
702 fd
= accept(server_fd
, (struct sockaddr
*)&from_addr
,
705 http_log("error during accept %s\n", strerror(errno
));
708 ff_socket_nonblock(fd
, 1);
710 /* XXX: should output a warning page when coming
711 close to the connection limit */
712 if (nb_connections
>= nb_max_connections
)
715 /* add a new connection */
716 c
= av_mallocz(sizeof(HTTPContext
));
721 c
->poll_entry
= NULL
;
722 c
->from_addr
= from_addr
;
723 c
->buffer_size
= IOBUFFER_INIT_SIZE
;
724 c
->buffer
= av_malloc(c
->buffer_size
);
728 c
->next
= first_http_ctx
;
732 start_wait_request(c
, is_rtsp
);
744 static void close_connection(HTTPContext
*c
)
746 HTTPContext
**cp
, *c1
;
748 AVFormatContext
*ctx
;
752 /* remove connection from list */
753 cp
= &first_http_ctx
;
754 while ((*cp
) != NULL
) {
762 /* remove references, if any (XXX: do it faster) */
763 for(c1
= first_http_ctx
; c1
!= NULL
; c1
= c1
->next
) {
768 /* remove connection associated resources */
772 /* close each frame parser */
773 for(i
=0;i
<c
->fmt_in
->nb_streams
;i
++) {
774 st
= c
->fmt_in
->streams
[i
];
775 if (st
->codec
->codec
)
776 avcodec_close(st
->codec
);
778 av_close_input_file(c
->fmt_in
);
781 /* free RTP output streams if any */
784 nb_streams
= c
->stream
->nb_streams
;
786 for(i
=0;i
<nb_streams
;i
++) {
789 av_write_trailer(ctx
);
792 h
= c
->rtp_handles
[i
];
799 if (!c
->last_packet_sent
&& c
->state
== HTTPSTATE_SEND_DATA_TRAILER
) {
802 if (url_open_dyn_buf(&ctx
->pb
) >= 0) {
803 av_write_trailer(ctx
);
804 av_freep(&c
->pb_buffer
);
805 url_close_dyn_buf(ctx
->pb
, &c
->pb_buffer
);
810 for(i
=0; i
<ctx
->nb_streams
; i
++)
811 av_free(ctx
->streams
[i
]);
813 if (c
->stream
&& !c
->post
&& c
->stream
->stream_type
== STREAM_TYPE_LIVE
)
814 current_bandwidth
-= c
->stream
->bandwidth
;
816 /* signal that there is no feed if we are the feeder socket */
817 if (c
->state
== HTTPSTATE_RECEIVE_DATA
&& c
->stream
) {
818 c
->stream
->feed_opened
= 0;
822 av_freep(&c
->pb_buffer
);
823 av_freep(&c
->packet_buffer
);
829 static int handle_connection(HTTPContext
*c
)
834 case HTTPSTATE_WAIT_REQUEST
:
835 case RTSPSTATE_WAIT_REQUEST
:
837 if ((c
->timeout
- cur_time
) < 0)
839 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
))
842 /* no need to read if no events */
843 if (!(c
->poll_entry
->revents
& POLLIN
))
847 len
= recv(c
->fd
, c
->buffer_ptr
, 1, 0);
849 if (ff_neterrno() != FF_NETERROR(EAGAIN
) &&
850 ff_neterrno() != FF_NETERROR(EINTR
))
852 } else if (len
== 0) {
855 /* search for end of request. */
857 c
->buffer_ptr
+= len
;
859 if ((ptr
>= c
->buffer
+ 2 && !memcmp(ptr
-2, "\n\n", 2)) ||
860 (ptr
>= c
->buffer
+ 4 && !memcmp(ptr
-4, "\r\n\r\n", 4))) {
861 /* request found : parse it and reply */
862 if (c
->state
== HTTPSTATE_WAIT_REQUEST
) {
863 ret
= http_parse_request(c
);
865 ret
= rtsp_parse_request(c
);
869 } else if (ptr
>= c
->buffer_end
) {
870 /* request too long: cannot do anything */
872 } else goto read_loop
;
876 case HTTPSTATE_SEND_HEADER
:
877 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
))
880 /* no need to write if no events */
881 if (!(c
->poll_entry
->revents
& POLLOUT
))
883 len
= send(c
->fd
, c
->buffer_ptr
, c
->buffer_end
- c
->buffer_ptr
, 0);
885 if (ff_neterrno() != FF_NETERROR(EAGAIN
) &&
886 ff_neterrno() != FF_NETERROR(EINTR
)) {
887 /* error : close connection */
888 av_freep(&c
->pb_buffer
);
892 c
->buffer_ptr
+= len
;
894 c
->stream
->bytes_served
+= len
;
895 c
->data_count
+= len
;
896 if (c
->buffer_ptr
>= c
->buffer_end
) {
897 av_freep(&c
->pb_buffer
);
901 /* all the buffer was sent : synchronize to the incoming stream */
902 c
->state
= HTTPSTATE_SEND_DATA_HEADER
;
903 c
->buffer_ptr
= c
->buffer_end
= c
->buffer
;
908 case HTTPSTATE_SEND_DATA
:
909 case HTTPSTATE_SEND_DATA_HEADER
:
910 case HTTPSTATE_SEND_DATA_TRAILER
:
911 /* for packetized output, we consider we can always write (the
912 input streams sets the speed). It may be better to verify
913 that we do not rely too much on the kernel queues */
914 if (!c
->is_packetized
) {
915 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
))
918 /* no need to read if no events */
919 if (!(c
->poll_entry
->revents
& POLLOUT
))
922 if (http_send_data(c
) < 0)
924 /* close connection if trailer sent */
925 if (c
->state
== HTTPSTATE_SEND_DATA_TRAILER
)
928 case HTTPSTATE_RECEIVE_DATA
:
929 /* no need to read if no events */
930 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
))
932 if (!(c
->poll_entry
->revents
& POLLIN
))
934 if (http_receive_data(c
) < 0)
937 case HTTPSTATE_WAIT_FEED
:
938 /* no need to read if no events */
939 if (c
->poll_entry
->revents
& (POLLIN
| POLLERR
| POLLHUP
))
942 /* nothing to do, we'll be waken up by incoming feed packets */
945 case RTSPSTATE_SEND_REPLY
:
946 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
)) {
947 av_freep(&c
->pb_buffer
);
950 /* no need to write if no events */
951 if (!(c
->poll_entry
->revents
& POLLOUT
))
953 len
= send(c
->fd
, c
->buffer_ptr
, c
->buffer_end
- c
->buffer_ptr
, 0);
955 if (ff_neterrno() != FF_NETERROR(EAGAIN
) &&
956 ff_neterrno() != FF_NETERROR(EINTR
)) {
957 /* error : close connection */
958 av_freep(&c
->pb_buffer
);
962 c
->buffer_ptr
+= len
;
963 c
->data_count
+= len
;
964 if (c
->buffer_ptr
>= c
->buffer_end
) {
965 /* all the buffer was sent : wait for a new request */
966 av_freep(&c
->pb_buffer
);
967 start_wait_request(c
, 1);
971 case RTSPSTATE_SEND_PACKET
:
972 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
)) {
973 av_freep(&c
->packet_buffer
);
976 /* no need to write if no events */
977 if (!(c
->poll_entry
->revents
& POLLOUT
))
979 len
= send(c
->fd
, c
->packet_buffer_ptr
,
980 c
->packet_buffer_end
- c
->packet_buffer_ptr
, 0);
982 if (ff_neterrno() != FF_NETERROR(EAGAIN
) &&
983 ff_neterrno() != FF_NETERROR(EINTR
)) {
984 /* error : close connection */
985 av_freep(&c
->packet_buffer
);
989 c
->packet_buffer_ptr
+= len
;
990 if (c
->packet_buffer_ptr
>= c
->packet_buffer_end
) {
991 /* all the buffer was sent : wait for a new request */
992 av_freep(&c
->packet_buffer
);
993 c
->state
= RTSPSTATE_WAIT_REQUEST
;
997 case HTTPSTATE_READY
:
1006 static int extract_rates(char *rates
, int ratelen
, const char *request
)
1010 for (p
= request
; *p
&& *p
!= '\r' && *p
!= '\n'; ) {
1011 if (strncasecmp(p
, "Pragma:", 7) == 0) {
1012 const char *q
= p
+ 7;
1014 while (*q
&& *q
!= '\n' && isspace(*q
))
1017 if (strncasecmp(q
, "stream-switch-entry=", 20) == 0) {
1023 memset(rates
, 0xff, ratelen
);
1026 while (*q
&& *q
!= '\n' && *q
!= ':')
1029 if (sscanf(q
, ":%d:%d", &stream_no
, &rate_no
) != 2)
1033 if (stream_no
< ratelen
&& stream_no
>= 0)
1034 rates
[stream_no
] = rate_no
;
1036 while (*q
&& *q
!= '\n' && !isspace(*q
))
1043 p
= strchr(p
, '\n');
1053 static int find_stream_in_feed(FFStream
*feed
, AVCodecContext
*codec
, int bit_rate
)
1056 int best_bitrate
= 100000000;
1059 for (i
= 0; i
< feed
->nb_streams
; i
++) {
1060 AVCodecContext
*feed_codec
= feed
->streams
[i
]->codec
;
1062 if (feed_codec
->codec_id
!= codec
->codec_id
||
1063 feed_codec
->sample_rate
!= codec
->sample_rate
||
1064 feed_codec
->width
!= codec
->width
||
1065 feed_codec
->height
!= codec
->height
)
1068 /* Potential stream */
1070 /* We want the fastest stream less than bit_rate, or the slowest
1071 * faster than bit_rate
1074 if (feed_codec
->bit_rate
<= bit_rate
) {
1075 if (best_bitrate
> bit_rate
|| feed_codec
->bit_rate
> best_bitrate
) {
1076 best_bitrate
= feed_codec
->bit_rate
;
1080 if (feed_codec
->bit_rate
< best_bitrate
) {
1081 best_bitrate
= feed_codec
->bit_rate
;
1090 static int modify_current_stream(HTTPContext
*c
, char *rates
)
1093 FFStream
*req
= c
->stream
;
1094 int action_required
= 0;
1096 /* Not much we can do for a feed */
1100 for (i
= 0; i
< req
->nb_streams
; i
++) {
1101 AVCodecContext
*codec
= req
->streams
[i
]->codec
;
1105 c
->switch_feed_streams
[i
] = req
->feed_streams
[i
];
1108 c
->switch_feed_streams
[i
] = find_stream_in_feed(req
->feed
, codec
, codec
->bit_rate
/ 2);
1111 /* Wants off or slow */
1112 c
->switch_feed_streams
[i
] = find_stream_in_feed(req
->feed
, codec
, codec
->bit_rate
/ 4);
1114 /* This doesn't work well when it turns off the only stream! */
1115 c
->switch_feed_streams
[i
] = -2;
1116 c
->feed_streams
[i
] = -2;
1121 if (c
->switch_feed_streams
[i
] >= 0 && c
->switch_feed_streams
[i
] != c
->feed_streams
[i
])
1122 action_required
= 1;
1125 return action_required
;
1129 static void do_switch_stream(HTTPContext
*c
, int i
)
1131 if (c
->switch_feed_streams
[i
] >= 0) {
1133 c
->feed_streams
[i
] = c
->switch_feed_streams
[i
];
1136 /* Now update the stream */
1138 c
->switch_feed_streams
[i
] = -1;
1141 /* XXX: factorize in utils.c ? */
1142 /* XXX: take care with different space meaning */
1143 static void skip_spaces(const char **pp
)
1147 while (*p
== ' ' || *p
== '\t')
1152 static void get_word(char *buf
, int buf_size
, const char **pp
)
1160 while (!isspace(*p
) && *p
!= '\0') {
1161 if ((q
- buf
) < buf_size
- 1)
1170 static int validate_acl(FFStream
*stream
, HTTPContext
*c
)
1172 enum IPAddressAction last_action
= IP_DENY
;
1174 struct in_addr
*src
= &c
->from_addr
.sin_addr
;
1175 unsigned long src_addr
= src
->s_addr
;
1177 for (acl
= stream
->acl
; acl
; acl
= acl
->next
) {
1178 if (src_addr
>= acl
->first
.s_addr
&& src_addr
<= acl
->last
.s_addr
)
1179 return (acl
->action
== IP_ALLOW
) ? 1 : 0;
1180 last_action
= acl
->action
;
1183 /* Nothing matched, so return not the last action */
1184 return (last_action
== IP_DENY
) ? 1 : 0;
1187 /* compute the real filename of a file by matching it without its
1188 extensions to all the stream filenames */
1189 static void compute_real_filename(char *filename
, int max_size
)
1196 /* compute filename by matching without the file extensions */
1197 av_strlcpy(file1
, filename
, sizeof(file1
));
1198 p
= strrchr(file1
, '.');
1201 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
1202 av_strlcpy(file2
, stream
->filename
, sizeof(file2
));
1203 p
= strrchr(file2
, '.');
1206 if (!strcmp(file1
, file2
)) {
1207 av_strlcpy(filename
, stream
->filename
, max_size
);
1222 /* parse http request and prepare header */
1223 static int http_parse_request(HTTPContext
*c
)
1226 enum RedirType redir_type
;
1228 char info
[1024], filename
[1024];
1232 const char *mime_type
;
1236 char *useragent
= 0;
1239 get_word(cmd
, sizeof(cmd
), (const char **)&p
);
1240 av_strlcpy(c
->method
, cmd
, sizeof(c
->method
));
1242 if (!strcmp(cmd
, "GET"))
1244 else if (!strcmp(cmd
, "POST"))
1249 get_word(url
, sizeof(url
), (const char **)&p
);
1250 av_strlcpy(c
->url
, url
, sizeof(c
->url
));
1252 get_word(protocol
, sizeof(protocol
), (const char **)&p
);
1253 if (strcmp(protocol
, "HTTP/1.0") && strcmp(protocol
, "HTTP/1.1"))
1256 av_strlcpy(c
->protocol
, protocol
, sizeof(c
->protocol
));
1259 http_log("New connection: %s %s\n", cmd
, url
);
1261 /* find the filename and the optional info string in the request */
1262 p
= strchr(url
, '?');
1264 av_strlcpy(info
, p
, sizeof(info
));
1269 av_strlcpy(filename
, url
+ ((*url
== '/') ? 1 : 0), sizeof(filename
)-1);
1271 for (p
= c
->buffer
; *p
&& *p
!= '\r' && *p
!= '\n'; ) {
1272 if (strncasecmp(p
, "User-Agent:", 11) == 0) {
1274 if (*useragent
&& *useragent
!= '\n' && isspace(*useragent
))
1278 p
= strchr(p
, '\n');
1285 redir_type
= REDIR_NONE
;
1286 if (match_ext(filename
, "asx")) {
1287 redir_type
= REDIR_ASX
;
1288 filename
[strlen(filename
)-1] = 'f';
1289 } else if (match_ext(filename
, "asf") &&
1290 (!useragent
|| strncasecmp(useragent
, "NSPlayer", 8) != 0)) {
1291 /* if this isn't WMP or lookalike, return the redirector file */
1292 redir_type
= REDIR_ASF
;
1293 } else if (match_ext(filename
, "rpm,ram")) {
1294 redir_type
= REDIR_RAM
;
1295 strcpy(filename
+ strlen(filename
)-2, "m");
1296 } else if (match_ext(filename
, "rtsp")) {
1297 redir_type
= REDIR_RTSP
;
1298 compute_real_filename(filename
, sizeof(filename
) - 1);
1299 } else if (match_ext(filename
, "sdp")) {
1300 redir_type
= REDIR_SDP
;
1301 compute_real_filename(filename
, sizeof(filename
) - 1);
1304 // "redirect" / request to index.html
1305 if (!strlen(filename
))
1306 av_strlcpy(filename
, "index.html", sizeof(filename
) - 1);
1308 stream
= first_stream
;
1309 while (stream
!= NULL
) {
1310 if (!strcmp(stream
->filename
, filename
) && validate_acl(stream
, c
))
1312 stream
= stream
->next
;
1314 if (stream
== NULL
) {
1315 snprintf(msg
, sizeof(msg
), "File '%s' not found", url
);
1320 memcpy(c
->feed_streams
, stream
->feed_streams
, sizeof(c
->feed_streams
));
1321 memset(c
->switch_feed_streams
, -1, sizeof(c
->switch_feed_streams
));
1323 if (stream
->stream_type
== STREAM_TYPE_REDIRECT
) {
1324 c
->http_error
= 301;
1326 q
+= snprintf(q
, c
->buffer_size
,
1327 "HTTP/1.0 301 Moved\r\n"
1329 "Content-type: text/html\r\n"
1331 "<html><head><title>Moved</title></head><body>\r\n"
1332 "You should be <a href=\"%s\">redirected</a>.\r\n"
1333 "</body></html>\r\n", stream
->feed_filename
, stream
->feed_filename
);
1334 /* prepare output buffer */
1335 c
->buffer_ptr
= c
->buffer
;
1337 c
->state
= HTTPSTATE_SEND_HEADER
;
1341 /* If this is WMP, get the rate information */
1342 if (extract_rates(ratebuf
, sizeof(ratebuf
), c
->buffer
)) {
1343 if (modify_current_stream(c
, ratebuf
)) {
1344 for (i
= 0; i
< FF_ARRAY_ELEMS(c
->feed_streams
); i
++) {
1345 if (c
->switch_feed_streams
[i
] >= 0)
1346 do_switch_stream(c
, i
);
1351 /* If already streaming this feed, do not let start another feeder. */
1352 if (stream
->feed_opened
) {
1353 snprintf(msg
, sizeof(msg
), "This feed is already being received.");
1354 http_log("feed %s already being received\n", stream
->feed_filename
);
1358 if (c
->post
== 0 && stream
->stream_type
== STREAM_TYPE_LIVE
)
1359 current_bandwidth
+= stream
->bandwidth
;
1361 if (c
->post
== 0 && max_bandwidth
< current_bandwidth
) {
1362 c
->http_error
= 200;
1364 q
+= snprintf(q
, c
->buffer_size
,
1365 "HTTP/1.0 200 Server too busy\r\n"
1366 "Content-type: text/html\r\n"
1368 "<html><head><title>Too busy</title></head><body>\r\n"
1369 "<p>The server is too busy to serve your request at this time.</p>\r\n"
1370 "<p>The bandwidth being served (including your stream) is %"PRIu64
"kbit/sec, "
1371 "and this exceeds the limit of %"PRIu64
"kbit/sec.</p>\r\n"
1372 "</body></html>\r\n", current_bandwidth
, max_bandwidth
);
1373 /* prepare output buffer */
1374 c
->buffer_ptr
= c
->buffer
;
1376 c
->state
= HTTPSTATE_SEND_HEADER
;
1380 if (redir_type
!= REDIR_NONE
) {
1383 for (p
= c
->buffer
; *p
&& *p
!= '\r' && *p
!= '\n'; ) {
1384 if (strncasecmp(p
, "Host:", 5) == 0) {
1388 p
= strchr(p
, '\n');
1399 while (isspace(*hostinfo
))
1402 eoh
= strchr(hostinfo
, '\n');
1404 if (eoh
[-1] == '\r')
1407 if (eoh
- hostinfo
< sizeof(hostbuf
) - 1) {
1408 memcpy(hostbuf
, hostinfo
, eoh
- hostinfo
);
1409 hostbuf
[eoh
- hostinfo
] = 0;
1411 c
->http_error
= 200;
1413 switch(redir_type
) {
1415 q
+= snprintf(q
, c
->buffer_size
,
1416 "HTTP/1.0 200 ASX Follows\r\n"
1417 "Content-type: video/x-ms-asf\r\n"
1419 "<ASX Version=\"3\">\r\n"
1420 //"<!-- Autogenerated by ffserver -->\r\n"
1421 "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n"
1422 "</ASX>\r\n", hostbuf
, filename
, info
);
1425 q
+= snprintf(q
, c
->buffer_size
,
1426 "HTTP/1.0 200 RAM Follows\r\n"
1427 "Content-type: audio/x-pn-realaudio\r\n"
1429 "# Autogenerated by ffserver\r\n"
1430 "http://%s/%s%s\r\n", hostbuf
, filename
, info
);
1433 q
+= snprintf(q
, c
->buffer_size
,
1434 "HTTP/1.0 200 ASF Redirect follows\r\n"
1435 "Content-type: video/x-ms-asf\r\n"
1438 "Ref1=http://%s/%s%s\r\n", hostbuf
, filename
, info
);
1442 char hostname
[256], *p
;
1443 /* extract only hostname */
1444 av_strlcpy(hostname
, hostbuf
, sizeof(hostname
));
1445 p
= strrchr(hostname
, ':');
1448 q
+= snprintf(q
, c
->buffer_size
,
1449 "HTTP/1.0 200 RTSP Redirect follows\r\n"
1450 /* XXX: incorrect mime type ? */
1451 "Content-type: application/x-rtsp\r\n"
1453 "rtsp://%s:%d/%s\r\n", hostname
, ntohs(my_rtsp_addr
.sin_port
), filename
);
1459 int sdp_data_size
, len
;
1460 struct sockaddr_in my_addr
;
1462 q
+= snprintf(q
, c
->buffer_size
,
1463 "HTTP/1.0 200 OK\r\n"
1464 "Content-type: application/sdp\r\n"
1467 len
= sizeof(my_addr
);
1468 getsockname(c
->fd
, (struct sockaddr
*)&my_addr
, &len
);
1470 /* XXX: should use a dynamic buffer */
1471 sdp_data_size
= prepare_sdp_description(stream
,
1474 if (sdp_data_size
> 0) {
1475 memcpy(q
, sdp_data
, sdp_data_size
);
1487 /* prepare output buffer */
1488 c
->buffer_ptr
= c
->buffer
;
1490 c
->state
= HTTPSTATE_SEND_HEADER
;
1496 snprintf(msg
, sizeof(msg
), "ASX/RAM file not handled");
1500 stream
->conns_served
++;
1502 /* XXX: add there authenticate and IP match */
1505 /* if post, it means a feed is being sent */
1506 if (!stream
->is_feed
) {
1507 /* However it might be a status report from WMP! Let us log the
1508 * data as it might come in handy one day. */
1512 for (p
= c
->buffer
; *p
&& *p
!= '\r' && *p
!= '\n'; ) {
1513 if (strncasecmp(p
, "Pragma: log-line=", 17) == 0) {
1517 if (strncasecmp(p
, "Pragma: client-id=", 18) == 0)
1518 client_id
= strtol(p
+ 18, 0, 10);
1519 p
= strchr(p
, '\n');
1527 char *eol
= strchr(logline
, '\n');
1532 if (eol
[-1] == '\r')
1534 http_log("%.*s\n", (int) (eol
- logline
), logline
);
1535 c
->suppress_log
= 1;
1540 http_log("\nGot request:\n%s\n", c
->buffer
);
1543 if (client_id
&& extract_rates(ratebuf
, sizeof(ratebuf
), c
->buffer
)) {
1546 /* Now we have to find the client_id */
1547 for (wmpc
= first_http_ctx
; wmpc
; wmpc
= wmpc
->next
) {
1548 if (wmpc
->wmp_client_id
== client_id
)
1552 if (wmpc
&& modify_current_stream(wmpc
, ratebuf
))
1553 wmpc
->switch_pending
= 1;
1556 snprintf(msg
, sizeof(msg
), "POST command not handled");
1560 if (http_start_receive_data(c
) < 0) {
1561 snprintf(msg
, sizeof(msg
), "could not open feed");
1565 c
->state
= HTTPSTATE_RECEIVE_DATA
;
1570 if (strcmp(stream
->filename
+ strlen(stream
->filename
) - 4, ".asf") == 0)
1571 http_log("\nGot request:\n%s\n", c
->buffer
);
1574 if (c
->stream
->stream_type
== STREAM_TYPE_STATUS
)
1577 /* open input stream */
1578 if (open_input_stream(c
, info
) < 0) {
1579 snprintf(msg
, sizeof(msg
), "Input stream corresponding to '%s' not found", url
);
1583 /* prepare http header */
1585 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "HTTP/1.0 200 OK\r\n");
1586 mime_type
= c
->stream
->fmt
->mime_type
;
1588 mime_type
= "application/x-octet-stream";
1589 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "Pragma: no-cache\r\n");
1591 /* for asf, we need extra headers */
1592 if (!strcmp(c
->stream
->fmt
->name
,"asf_stream")) {
1593 /* Need to allocate a client id */
1595 c
->wmp_client_id
= av_random(&random_state
) & 0x7fffffff;
1597 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
);
1599 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "Content-Type: %s\r\n", mime_type
);
1600 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "\r\n");
1602 /* prepare output buffer */
1604 c
->buffer_ptr
= c
->buffer
;
1606 c
->state
= HTTPSTATE_SEND_HEADER
;
1609 c
->http_error
= 404;
1611 q
+= snprintf(q
, c
->buffer_size
,
1612 "HTTP/1.0 404 Not Found\r\n"
1613 "Content-type: text/html\r\n"
1616 "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n"
1619 /* prepare output buffer */
1620 c
->buffer_ptr
= c
->buffer
;
1622 c
->state
= HTTPSTATE_SEND_HEADER
;
1626 c
->http_error
= 200; /* horrible : we use this value to avoid
1627 going to the send data state */
1628 c
->state
= HTTPSTATE_SEND_HEADER
;
1632 static void fmt_bytecount(ByteIOContext
*pb
, int64_t count
)
1634 static const char *suffix
= " kMGTP";
1637 for (s
= suffix
; count
>= 100000 && s
[1]; count
/= 1000, s
++);
1639 url_fprintf(pb
, "%"PRId64
"%c", count
, *s
);
1642 static void compute_status(HTTPContext
*c
)
1651 if (url_open_dyn_buf(&pb
) < 0) {
1652 /* XXX: return an error ? */
1653 c
->buffer_ptr
= c
->buffer
;
1654 c
->buffer_end
= c
->buffer
;
1658 url_fprintf(pb
, "HTTP/1.0 200 OK\r\n");
1659 url_fprintf(pb
, "Content-type: %s\r\n", "text/html");
1660 url_fprintf(pb
, "Pragma: no-cache\r\n");
1661 url_fprintf(pb
, "\r\n");
1663 url_fprintf(pb
, "<HTML><HEAD><TITLE>%s Status</TITLE>\n", program_name
);
1664 if (c
->stream
->feed_filename
[0])
1665 url_fprintf(pb
, "<link rel=\"shortcut icon\" href=\"%s\">\n", c
->stream
->feed_filename
);
1666 url_fprintf(pb
, "</HEAD>\n<BODY>");
1667 url_fprintf(pb
, "<H1>%s Status</H1>\n", program_name
);
1669 url_fprintf(pb
, "<H2>Available Streams</H2>\n");
1670 url_fprintf(pb
, "<TABLE cellspacing=0 cellpadding=4>\n");
1671 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");
1672 stream
= first_stream
;
1673 while (stream
!= NULL
) {
1674 char sfilename
[1024];
1677 if (stream
->feed
!= stream
) {
1678 av_strlcpy(sfilename
, stream
->filename
, sizeof(sfilename
) - 10);
1679 eosf
= sfilename
+ strlen(sfilename
);
1680 if (eosf
- sfilename
>= 4) {
1681 if (strcmp(eosf
- 4, ".asf") == 0)
1682 strcpy(eosf
- 4, ".asx");
1683 else if (strcmp(eosf
- 3, ".rm") == 0)
1684 strcpy(eosf
- 3, ".ram");
1685 else if (stream
->fmt
&& !strcmp(stream
->fmt
->name
, "rtp")) {
1686 /* generate a sample RTSP director if
1687 unicast. Generate an SDP redirector if
1689 eosf
= strrchr(sfilename
, '.');
1691 eosf
= sfilename
+ strlen(sfilename
);
1692 if (stream
->is_multicast
)
1693 strcpy(eosf
, ".sdp");
1695 strcpy(eosf
, ".rtsp");
1699 url_fprintf(pb
, "<TR><TD><A HREF=\"/%s\">%s</A> ",
1700 sfilename
, stream
->filename
);
1701 url_fprintf(pb
, "<td align=right> %d <td align=right> ",
1702 stream
->conns_served
);
1703 fmt_bytecount(pb
, stream
->bytes_served
);
1704 switch(stream
->stream_type
) {
1705 case STREAM_TYPE_LIVE
: {
1706 int audio_bit_rate
= 0;
1707 int video_bit_rate
= 0;
1708 const char *audio_codec_name
= "";
1709 const char *video_codec_name
= "";
1710 const char *audio_codec_name_extra
= "";
1711 const char *video_codec_name_extra
= "";
1713 for(i
=0;i
<stream
->nb_streams
;i
++) {
1714 AVStream
*st
= stream
->streams
[i
];
1715 AVCodec
*codec
= avcodec_find_encoder(st
->codec
->codec_id
);
1716 switch(st
->codec
->codec_type
) {
1717 case CODEC_TYPE_AUDIO
:
1718 audio_bit_rate
+= st
->codec
->bit_rate
;
1720 if (*audio_codec_name
)
1721 audio_codec_name_extra
= "...";
1722 audio_codec_name
= codec
->name
;
1725 case CODEC_TYPE_VIDEO
:
1726 video_bit_rate
+= st
->codec
->bit_rate
;
1728 if (*video_codec_name
)
1729 video_codec_name_extra
= "...";
1730 video_codec_name
= codec
->name
;
1733 case CODEC_TYPE_DATA
:
1734 video_bit_rate
+= st
->codec
->bit_rate
;
1740 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",
1743 video_bit_rate
/ 1000, video_codec_name
, video_codec_name_extra
,
1744 audio_bit_rate
/ 1000, audio_codec_name
, audio_codec_name_extra
);
1746 url_fprintf(pb
, "<TD>%s", stream
->feed
->filename
);
1748 url_fprintf(pb
, "<TD>%s", stream
->feed_filename
);
1749 url_fprintf(pb
, "\n");
1753 url_fprintf(pb
, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
1757 stream
= stream
->next
;
1759 url_fprintf(pb
, "</TABLE>\n");
1761 stream
= first_stream
;
1762 while (stream
!= NULL
) {
1763 if (stream
->feed
== stream
) {
1764 url_fprintf(pb
, "<h2>Feed %s</h2>", stream
->filename
);
1766 url_fprintf(pb
, "Running as pid %d.\n", stream
->pid
);
1768 #if defined(linux) && !defined(CONFIG_NOCUTILS)
1773 /* This is somewhat linux specific I guess */
1774 snprintf(ps_cmd
, sizeof(ps_cmd
),
1775 "ps -o \"%%cpu,cputime\" --no-headers %d",
1778 pid_stat
= popen(ps_cmd
, "r");
1783 if (fscanf(pid_stat
, "%10s %64s", cpuperc
,
1785 url_fprintf(pb
, "Currently using %s%% of the cpu. Total time used %s.\n",
1793 url_fprintf(pb
, "<p>");
1795 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");
1797 for (i
= 0; i
< stream
->nb_streams
; i
++) {
1798 AVStream
*st
= stream
->streams
[i
];
1799 AVCodec
*codec
= avcodec_find_encoder(st
->codec
->codec_id
);
1800 const char *type
= "unknown";
1801 char parameters
[64];
1805 switch(st
->codec
->codec_type
) {
1806 case CODEC_TYPE_AUDIO
:
1808 snprintf(parameters
, sizeof(parameters
), "%d channel(s), %d Hz", st
->codec
->channels
, st
->codec
->sample_rate
);
1810 case CODEC_TYPE_VIDEO
:
1812 snprintf(parameters
, sizeof(parameters
), "%dx%d, q=%d-%d, fps=%d", st
->codec
->width
, st
->codec
->height
,
1813 st
->codec
->qmin
, st
->codec
->qmax
, st
->codec
->time_base
.den
/ st
->codec
->time_base
.num
);
1818 url_fprintf(pb
, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
1819 i
, type
, st
->codec
->bit_rate
/1000, codec
? codec
->name
: "", parameters
);
1821 url_fprintf(pb
, "</table>\n");
1824 stream
= stream
->next
;
1830 AVCodecContext
*enc
;
1834 stream
= first_feed
;
1835 while (stream
!= NULL
) {
1836 url_fprintf(pb
, "<H1>Feed '%s'</H1>\n", stream
->filename
);
1837 url_fprintf(pb
, "<TABLE>\n");
1838 url_fprintf(pb
, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n");
1839 for(i
=0;i
<stream
->nb_streams
;i
++) {
1840 AVStream
*st
= stream
->streams
[i
];
1841 FeedData
*fdata
= st
->priv_data
;
1844 avcodec_string(buf
, sizeof(buf
), enc
);
1845 avg
= fdata
->avg_frame_size
* (float)enc
->rate
* 8.0;
1846 if (enc
->codec
->type
== CODEC_TYPE_AUDIO
&& enc
->frame_size
> 0)
1847 avg
/= enc
->frame_size
;
1848 url_fprintf(pb
, "<TR><TD>%s <TD> %d <TD> %"PRId64
" <TD> %0.1f\n",
1849 buf
, enc
->frame_number
, fdata
->data_count
, avg
/ 1000.0);
1851 url_fprintf(pb
, "</TABLE>\n");
1852 stream
= stream
->next_feed
;
1857 /* connection status */
1858 url_fprintf(pb
, "<H2>Connection Status</H2>\n");
1860 url_fprintf(pb
, "Number of connections: %d / %d<BR>\n",
1861 nb_connections
, nb_max_connections
);
1863 url_fprintf(pb
, "Bandwidth in use: %"PRIu64
"k / %"PRIu64
"k<BR>\n",
1864 current_bandwidth
, max_bandwidth
);
1866 url_fprintf(pb
, "<TABLE>\n");
1867 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");
1868 c1
= first_http_ctx
;
1870 while (c1
!= NULL
) {
1876 for (j
= 0; j
< c1
->stream
->nb_streams
; j
++) {
1877 if (!c1
->stream
->feed
)
1878 bitrate
+= c1
->stream
->streams
[j
]->codec
->bit_rate
;
1879 else if (c1
->feed_streams
[j
] >= 0)
1880 bitrate
+= c1
->stream
->feed
->streams
[c1
->feed_streams
[j
]]->codec
->bit_rate
;
1885 p
= inet_ntoa(c1
->from_addr
.sin_addr
);
1886 url_fprintf(pb
, "<TR><TD><B>%d</B><TD>%s%s<TD>%s<TD>%s<TD>%s<td align=right>",
1888 c1
->stream
? c1
->stream
->filename
: "",
1889 c1
->state
== HTTPSTATE_RECEIVE_DATA
? "(input)" : "",
1892 http_state
[c1
->state
]);
1893 fmt_bytecount(pb
, bitrate
);
1894 url_fprintf(pb
, "<td align=right>");
1895 fmt_bytecount(pb
, compute_datarate(&c1
->datarate
, c1
->data_count
) * 8);
1896 url_fprintf(pb
, "<td align=right>");
1897 fmt_bytecount(pb
, c1
->data_count
);
1898 url_fprintf(pb
, "\n");
1901 url_fprintf(pb
, "</TABLE>\n");
1906 url_fprintf(pb
, "<HR size=1 noshade>Generated at %s", p
);
1907 url_fprintf(pb
, "</BODY>\n</HTML>\n");
1909 len
= url_close_dyn_buf(pb
, &c
->pb_buffer
);
1910 c
->buffer_ptr
= c
->pb_buffer
;
1911 c
->buffer_end
= c
->pb_buffer
+ len
;
1914 /* check if the parser needs to be opened for stream i */
1915 static void open_parser(AVFormatContext
*s
, int i
)
1917 AVStream
*st
= s
->streams
[i
];
1920 if (!st
->codec
->codec
) {
1921 codec
= avcodec_find_decoder(st
->codec
->codec_id
);
1922 if (codec
&& (codec
->capabilities
& CODEC_CAP_PARSE_ONLY
)) {
1923 st
->codec
->parse_only
= 1;
1924 if (avcodec_open(st
->codec
, codec
) < 0)
1925 st
->codec
->parse_only
= 0;
1930 static int open_input_stream(HTTPContext
*c
, const char *info
)
1933 char input_filename
[1024];
1935 int buf_size
, i
, ret
;
1938 /* find file name */
1939 if (c
->stream
->feed
) {
1940 strcpy(input_filename
, c
->stream
->feed
->feed_filename
);
1941 buf_size
= FFM_PACKET_SIZE
;
1942 /* compute position (absolute time) */
1943 if (find_info_tag(buf
, sizeof(buf
), "date", info
)) {
1944 stream_pos
= parse_date(buf
, 0);
1945 if (stream_pos
== INT64_MIN
)
1947 } else if (find_info_tag(buf
, sizeof(buf
), "buffer", info
)) {
1948 int prebuffer
= strtol(buf
, 0, 10);
1949 stream_pos
= av_gettime() - prebuffer
* (int64_t)1000000;
1951 stream_pos
= av_gettime() - c
->stream
->prebuffer
* (int64_t)1000;
1953 strcpy(input_filename
, c
->stream
->feed_filename
);
1955 /* compute position (relative time) */
1956 if (find_info_tag(buf
, sizeof(buf
), "date", info
)) {
1957 stream_pos
= parse_date(buf
, 1);
1958 if (stream_pos
== INT64_MIN
)
1963 if (input_filename
[0] == '\0')
1967 { time_t when
= stream_pos
/ 1000000;
1968 http_log("Stream pos = %"PRId64
", time=%s", stream_pos
, ctime(&when
));
1973 if ((ret
= av_open_input_file(&s
, input_filename
, c
->stream
->ifmt
,
1974 buf_size
, c
->stream
->ap_in
)) < 0) {
1975 http_log("could not open %s: %d\n", input_filename
, ret
);
1978 s
->flags
|= AVFMT_FLAG_GENPTS
;
1980 av_find_stream_info(c
->fmt_in
);
1982 /* open each parser */
1983 for(i
=0;i
<s
->nb_streams
;i
++)
1986 /* choose stream as clock source (we favorize video stream if
1987 present) for packet sending */
1988 c
->pts_stream_index
= 0;
1989 for(i
=0;i
<c
->stream
->nb_streams
;i
++) {
1990 if (c
->pts_stream_index
== 0 &&
1991 c
->stream
->streams
[i
]->codec
->codec_type
== CODEC_TYPE_VIDEO
) {
1992 c
->pts_stream_index
= i
;
1997 if (c
->fmt_in
->iformat
->read_seek
)
1998 av_seek_frame(c
->fmt_in
, -1, stream_pos
, 0);
2000 /* set the start time (needed for maxtime and RTP packet timing) */
2001 c
->start_time
= cur_time
;
2002 c
->first_pts
= AV_NOPTS_VALUE
;
2006 /* return the server clock (in us) */
2007 static int64_t get_server_clock(HTTPContext
*c
)
2009 /* compute current pts value from system time */
2010 return (cur_time
- c
->start_time
) * 1000;
2013 /* return the estimated time at which the current packet must be sent
2015 static int64_t get_packet_send_clock(HTTPContext
*c
)
2017 int bytes_left
, bytes_sent
, frame_bytes
;
2019 frame_bytes
= c
->cur_frame_bytes
;
2020 if (frame_bytes
<= 0)
2023 bytes_left
= c
->buffer_end
- c
->buffer_ptr
;
2024 bytes_sent
= frame_bytes
- bytes_left
;
2025 return c
->cur_pts
+ (c
->cur_frame_duration
* bytes_sent
) / frame_bytes
;
2030 static int http_prepare_data(HTTPContext
*c
)
2033 AVFormatContext
*ctx
;
2035 av_freep(&c
->pb_buffer
);
2037 case HTTPSTATE_SEND_DATA_HEADER
:
2038 memset(&c
->fmt_ctx
, 0, sizeof(c
->fmt_ctx
));
2039 av_strlcpy(c
->fmt_ctx
.author
, c
->stream
->author
,
2040 sizeof(c
->fmt_ctx
.author
));
2041 av_strlcpy(c
->fmt_ctx
.comment
, c
->stream
->comment
,
2042 sizeof(c
->fmt_ctx
.comment
));
2043 av_strlcpy(c
->fmt_ctx
.copyright
, c
->stream
->copyright
,
2044 sizeof(c
->fmt_ctx
.copyright
));
2045 av_strlcpy(c
->fmt_ctx
.title
, c
->stream
->title
,
2046 sizeof(c
->fmt_ctx
.title
));
2048 for(i
=0;i
<c
->stream
->nb_streams
;i
++) {
2051 st
= av_mallocz(sizeof(AVStream
));
2052 c
->fmt_ctx
.streams
[i
] = st
;
2053 /* if file or feed, then just take streams from FFStream struct */
2054 if (!c
->stream
->feed
||
2055 c
->stream
->feed
== c
->stream
)
2056 src
= c
->stream
->streams
[i
];
2058 src
= c
->stream
->feed
->streams
[c
->stream
->feed_streams
[i
]];
2062 st
->codec
->frame_number
= 0; /* XXX: should be done in
2063 AVStream, not in codec */
2065 /* set output format parameters */
2066 c
->fmt_ctx
.oformat
= c
->stream
->fmt
;
2067 c
->fmt_ctx
.nb_streams
= c
->stream
->nb_streams
;
2069 c
->got_key_frame
= 0;
2071 /* prepare header and save header data in a stream */
2072 if (url_open_dyn_buf(&c
->fmt_ctx
.pb
) < 0) {
2073 /* XXX: potential leak */
2076 c
->fmt_ctx
.pb
->is_streamed
= 1;
2079 * HACK to avoid mpeg ps muxer to spit many underflow errors
2080 * Default value from FFmpeg
2081 * Try to set it use configuration option
2083 c
->fmt_ctx
.preload
= (int)(0.5*AV_TIME_BASE
);
2084 c
->fmt_ctx
.max_delay
= (int)(0.7*AV_TIME_BASE
);
2086 av_set_parameters(&c
->fmt_ctx
, NULL
);
2087 if (av_write_header(&c
->fmt_ctx
) < 0) {
2088 http_log("Error writing output header\n");
2092 len
= url_close_dyn_buf(c
->fmt_ctx
.pb
, &c
->pb_buffer
);
2093 c
->buffer_ptr
= c
->pb_buffer
;
2094 c
->buffer_end
= c
->pb_buffer
+ len
;
2096 c
->state
= HTTPSTATE_SEND_DATA
;
2097 c
->last_packet_sent
= 0;
2099 case HTTPSTATE_SEND_DATA
:
2100 /* find a new packet */
2101 /* read a packet from the input stream */
2102 if (c
->stream
->feed
)
2103 ffm_set_write_index(c
->fmt_in
,
2104 c
->stream
->feed
->feed_write_index
,
2105 c
->stream
->feed
->feed_size
);
2107 if (c
->stream
->max_time
&&
2108 c
->stream
->max_time
+ c
->start_time
- cur_time
< 0)
2109 /* We have timed out */
2110 c
->state
= HTTPSTATE_SEND_DATA_TRAILER
;
2114 if (av_read_frame(c
->fmt_in
, &pkt
) < 0) {
2115 if (c
->stream
->feed
&& c
->stream
->feed
->feed_opened
) {
2116 /* if coming from feed, it means we reached the end of the
2117 ffm file, so must wait for more data */
2118 c
->state
= HTTPSTATE_WAIT_FEED
;
2119 return 1; /* state changed */
2121 if (c
->stream
->loop
) {
2122 av_close_input_file(c
->fmt_in
);
2124 if (open_input_stream(c
, "") < 0)
2129 /* must send trailer now because eof or error */
2130 c
->state
= HTTPSTATE_SEND_DATA_TRAILER
;
2134 int source_index
= pkt
.stream_index
;
2135 /* update first pts if needed */
2136 if (c
->first_pts
== AV_NOPTS_VALUE
) {
2137 c
->first_pts
= av_rescale_q(pkt
.dts
, c
->fmt_in
->streams
[pkt
.stream_index
]->time_base
, AV_TIME_BASE_Q
);
2138 c
->start_time
= cur_time
;
2140 /* send it to the appropriate stream */
2141 if (c
->stream
->feed
) {
2142 /* if coming from a feed, select the right stream */
2143 if (c
->switch_pending
) {
2144 c
->switch_pending
= 0;
2145 for(i
=0;i
<c
->stream
->nb_streams
;i
++) {
2146 if (c
->switch_feed_streams
[i
] == pkt
.stream_index
)
2147 if (pkt
.flags
& PKT_FLAG_KEY
)
2148 do_switch_stream(c
, i
);
2149 if (c
->switch_feed_streams
[i
] >= 0)
2150 c
->switch_pending
= 1;
2153 for(i
=0;i
<c
->stream
->nb_streams
;i
++) {
2154 if (c
->feed_streams
[i
] == pkt
.stream_index
) {
2155 AVStream
*st
= c
->fmt_in
->streams
[source_index
];
2156 pkt
.stream_index
= i
;
2157 if (pkt
.flags
& PKT_FLAG_KEY
&&
2158 (st
->codec
->codec_type
== CODEC_TYPE_VIDEO
||
2159 c
->stream
->nb_streams
== 1))
2160 c
->got_key_frame
= 1;
2161 if (!c
->stream
->send_on_key
|| c
->got_key_frame
)
2166 AVCodecContext
*codec
;
2167 AVStream
*ist
, *ost
;
2169 ist
= c
->fmt_in
->streams
[source_index
];
2170 /* specific handling for RTP: we use several
2171 output stream (one for each RTP
2172 connection). XXX: need more abstract handling */
2173 if (c
->is_packetized
) {
2174 /* compute send time and duration */
2175 c
->cur_pts
= av_rescale_q(pkt
.dts
, ist
->time_base
, AV_TIME_BASE_Q
);
2176 if (ist
->start_time
!= AV_NOPTS_VALUE
)
2177 c
->cur_pts
-= av_rescale_q(ist
->start_time
, ist
->time_base
, AV_TIME_BASE_Q
);
2178 c
->cur_frame_duration
= av_rescale_q(pkt
.duration
, ist
->time_base
, AV_TIME_BASE_Q
);
2180 printf("index=%d pts=%0.3f duration=%0.6f\n",
2182 (double)c
->cur_pts
/
2184 (double)c
->cur_frame_duration
/
2187 /* find RTP context */
2188 c
->packet_stream_index
= pkt
.stream_index
;
2189 ctx
= c
->rtp_ctx
[c
->packet_stream_index
];
2191 av_free_packet(&pkt
);
2194 codec
= ctx
->streams
[0]->codec
;
2195 /* only one stream per RTP connection */
2196 pkt
.stream_index
= 0;
2200 codec
= ctx
->streams
[pkt
.stream_index
]->codec
;
2203 if (c
->is_packetized
) {
2204 int max_packet_size
;
2205 if (c
->rtp_protocol
== RTSP_LOWER_TRANSPORT_TCP
)
2206 max_packet_size
= RTSP_TCP_MAX_PACKET_SIZE
;
2208 max_packet_size
= url_get_max_packet_size(c
->rtp_handles
[c
->packet_stream_index
]);
2209 ret
= url_open_dyn_packet_buf(&ctx
->pb
, max_packet_size
);
2211 ret
= url_open_dyn_buf(&ctx
->pb
);
2214 /* XXX: potential leak */
2217 ost
= ctx
->streams
[pkt
.stream_index
];
2219 ctx
->pb
->is_streamed
= 1;
2220 if (pkt
.dts
!= AV_NOPTS_VALUE
)
2221 pkt
.dts
= av_rescale_q(pkt
.dts
, ist
->time_base
, ost
->time_base
);
2222 if (pkt
.pts
!= AV_NOPTS_VALUE
)
2223 pkt
.pts
= av_rescale_q(pkt
.pts
, ist
->time_base
, ost
->time_base
);
2224 pkt
.duration
= av_rescale_q(pkt
.duration
, ist
->time_base
, ost
->time_base
);
2225 if (av_write_frame(ctx
, &pkt
) < 0) {
2226 http_log("Error writing frame to output\n");
2227 c
->state
= HTTPSTATE_SEND_DATA_TRAILER
;
2230 len
= url_close_dyn_buf(ctx
->pb
, &c
->pb_buffer
);
2231 c
->cur_frame_bytes
= len
;
2232 c
->buffer_ptr
= c
->pb_buffer
;
2233 c
->buffer_end
= c
->pb_buffer
+ len
;
2235 codec
->frame_number
++;
2237 av_free_packet(&pkt
);
2241 av_free_packet(&pkt
);
2246 case HTTPSTATE_SEND_DATA_TRAILER
:
2247 /* last packet test ? */
2248 if (c
->last_packet_sent
|| c
->is_packetized
)
2251 /* prepare header */
2252 if (url_open_dyn_buf(&ctx
->pb
) < 0) {
2253 /* XXX: potential leak */
2256 c
->fmt_ctx
.pb
->is_streamed
= 1;
2257 av_write_trailer(ctx
);
2258 len
= url_close_dyn_buf(ctx
->pb
, &c
->pb_buffer
);
2259 c
->buffer_ptr
= c
->pb_buffer
;
2260 c
->buffer_end
= c
->pb_buffer
+ len
;
2262 c
->last_packet_sent
= 1;
2268 /* should convert the format at the same time */
2269 /* send data starting at c->buffer_ptr to the output connection
2270 (either UDP or TCP connection) */
2271 static int http_send_data(HTTPContext
*c
)
2276 if (c
->buffer_ptr
>= c
->buffer_end
) {
2277 ret
= http_prepare_data(c
);
2281 /* state change requested */
2284 if (c
->is_packetized
) {
2285 /* RTP data output */
2286 len
= c
->buffer_end
- c
->buffer_ptr
;
2288 /* fail safe - should never happen */
2290 c
->buffer_ptr
= c
->buffer_end
;
2293 len
= (c
->buffer_ptr
[0] << 24) |
2294 (c
->buffer_ptr
[1] << 16) |
2295 (c
->buffer_ptr
[2] << 8) |
2297 if (len
> (c
->buffer_end
- c
->buffer_ptr
))
2299 if ((get_packet_send_clock(c
) - get_server_clock(c
)) > 0) {
2300 /* nothing to send yet: we can wait */
2304 c
->data_count
+= len
;
2305 update_datarate(&c
->datarate
, c
->data_count
);
2307 c
->stream
->bytes_served
+= len
;
2309 if (c
->rtp_protocol
== RTSP_LOWER_TRANSPORT_TCP
) {
2310 /* RTP packets are sent inside the RTSP TCP connection */
2312 int interleaved_index
, size
;
2314 HTTPContext
*rtsp_c
;
2317 /* if no RTSP connection left, error */
2320 /* if already sending something, then wait. */
2321 if (rtsp_c
->state
!= RTSPSTATE_WAIT_REQUEST
)
2323 if (url_open_dyn_buf(&pb
) < 0)
2325 interleaved_index
= c
->packet_stream_index
* 2;
2326 /* RTCP packets are sent at odd indexes */
2327 if (c
->buffer_ptr
[1] == 200)
2328 interleaved_index
++;
2329 /* write RTSP TCP header */
2331 header
[1] = interleaved_index
;
2332 header
[2] = len
>> 8;
2334 put_buffer(pb
, header
, 4);
2335 /* write RTP packet data */
2337 put_buffer(pb
, c
->buffer_ptr
, len
);
2338 size
= url_close_dyn_buf(pb
, &c
->packet_buffer
);
2339 /* prepare asynchronous TCP sending */
2340 rtsp_c
->packet_buffer_ptr
= c
->packet_buffer
;
2341 rtsp_c
->packet_buffer_end
= c
->packet_buffer
+ size
;
2342 c
->buffer_ptr
+= len
;
2344 /* send everything we can NOW */
2345 len
= send(rtsp_c
->fd
, rtsp_c
->packet_buffer_ptr
,
2346 rtsp_c
->packet_buffer_end
- rtsp_c
->packet_buffer_ptr
, 0);
2348 rtsp_c
->packet_buffer_ptr
+= len
;
2349 if (rtsp_c
->packet_buffer_ptr
< rtsp_c
->packet_buffer_end
) {
2350 /* if we could not send all the data, we will
2351 send it later, so a new state is needed to
2352 "lock" the RTSP TCP connection */
2353 rtsp_c
->state
= RTSPSTATE_SEND_PACKET
;
2356 /* all data has been sent */
2357 av_freep(&c
->packet_buffer
);
2359 /* send RTP packet directly in UDP */
2361 url_write(c
->rtp_handles
[c
->packet_stream_index
],
2362 c
->buffer_ptr
, len
);
2363 c
->buffer_ptr
+= len
;
2364 /* here we continue as we can send several packets per 10 ms slot */
2367 /* TCP data output */
2368 len
= send(c
->fd
, c
->buffer_ptr
, c
->buffer_end
- c
->buffer_ptr
, 0);
2370 if (ff_neterrno() != FF_NETERROR(EAGAIN
) &&
2371 ff_neterrno() != FF_NETERROR(EINTR
))
2372 /* error : close connection */
2377 c
->buffer_ptr
+= len
;
2379 c
->data_count
+= len
;
2380 update_datarate(&c
->datarate
, c
->data_count
);
2382 c
->stream
->bytes_served
+= len
;
2390 static int http_start_receive_data(HTTPContext
*c
)
2394 if (c
->stream
->feed_opened
)
2397 /* Don't permit writing to this one */
2398 if (c
->stream
->readonly
)
2402 fd
= open(c
->stream
->feed_filename
, O_RDWR
);
2404 http_log("Error opening feeder file: %s\n", strerror(errno
));
2409 c
->stream
->feed_write_index
= ffm_read_write_index(fd
);
2410 c
->stream
->feed_size
= lseek(fd
, 0, SEEK_END
);
2411 lseek(fd
, 0, SEEK_SET
);
2413 /* init buffer input */
2414 c
->buffer_ptr
= c
->buffer
;
2415 c
->buffer_end
= c
->buffer
+ FFM_PACKET_SIZE
;
2416 c
->stream
->feed_opened
= 1;
2420 static int http_receive_data(HTTPContext
*c
)
2424 if (c
->buffer_end
> c
->buffer_ptr
) {
2427 len
= recv(c
->fd
, c
->buffer_ptr
, c
->buffer_end
- c
->buffer_ptr
, 0);
2429 if (ff_neterrno() != FF_NETERROR(EAGAIN
) &&
2430 ff_neterrno() != FF_NETERROR(EINTR
))
2431 /* error : close connection */
2433 } else if (len
== 0)
2434 /* end of connection : close it */
2437 c
->buffer_ptr
+= len
;
2438 c
->data_count
+= len
;
2439 update_datarate(&c
->datarate
, c
->data_count
);
2443 if (c
->buffer_ptr
- c
->buffer
>= 2 && c
->data_count
> FFM_PACKET_SIZE
) {
2444 if (c
->buffer
[0] != 'f' ||
2445 c
->buffer
[1] != 'm') {
2446 http_log("Feed stream has become desynchronized -- disconnecting\n");
2451 if (c
->buffer_ptr
>= c
->buffer_end
) {
2452 FFStream
*feed
= c
->stream
;
2453 /* a packet has been received : write it in the store, except
2455 if (c
->data_count
> FFM_PACKET_SIZE
) {
2457 // printf("writing pos=0x%"PRIx64" size=0x%"PRIx64"\n", feed->feed_write_index, feed->feed_size);
2458 /* XXX: use llseek or url_seek */
2459 lseek(c
->feed_fd
, feed
->feed_write_index
, SEEK_SET
);
2460 if (write(c
->feed_fd
, c
->buffer
, FFM_PACKET_SIZE
) < 0) {
2461 http_log("Error writing to feed file: %s\n", strerror(errno
));
2465 feed
->feed_write_index
+= FFM_PACKET_SIZE
;
2466 /* update file size */
2467 if (feed
->feed_write_index
> c
->stream
->feed_size
)
2468 feed
->feed_size
= feed
->feed_write_index
;
2470 /* handle wrap around if max file size reached */
2471 if (c
->stream
->feed_max_size
&& feed
->feed_write_index
>= c
->stream
->feed_max_size
)
2472 feed
->feed_write_index
= FFM_PACKET_SIZE
;
2475 ffm_write_write_index(c
->feed_fd
, feed
->feed_write_index
);
2477 /* wake up any waiting connections */
2478 for(c1
= first_http_ctx
; c1
!= NULL
; c1
= c1
->next
) {
2479 if (c1
->state
== HTTPSTATE_WAIT_FEED
&&
2480 c1
->stream
->feed
== c
->stream
->feed
)
2481 c1
->state
= HTTPSTATE_SEND_DATA
;
2484 /* We have a header in our hands that contains useful data */
2485 AVFormatContext
*s
= NULL
;
2487 AVInputFormat
*fmt_in
;
2490 /* use feed output format name to find corresponding input format */
2491 fmt_in
= av_find_input_format(feed
->fmt
->name
);
2495 url_open_buf(&pb
, c
->buffer
, c
->buffer_end
- c
->buffer
, URL_RDONLY
);
2496 pb
->is_streamed
= 1;
2498 if (av_open_input_stream(&s
, pb
, c
->stream
->feed_filename
, fmt_in
, NULL
) < 0) {
2503 /* Now we have the actual streams */
2504 if (s
->nb_streams
!= feed
->nb_streams
) {
2505 av_close_input_stream(s
);
2510 for (i
= 0; i
< s
->nb_streams
; i
++) {
2511 AVStream
*fst
= feed
->streams
[i
];
2512 AVStream
*st
= s
->streams
[i
];
2513 memcpy(fst
->codec
, st
->codec
, sizeof(AVCodecContext
));
2514 if (fst
->codec
->extradata_size
) {
2515 fst
->codec
->extradata
= av_malloc(fst
->codec
->extradata_size
);
2516 if (!fst
->codec
->extradata
)
2518 memcpy(fst
->codec
->extradata
, st
->codec
->extradata
,
2519 fst
->codec
->extradata_size
);
2523 av_close_input_stream(s
);
2526 c
->buffer_ptr
= c
->buffer
;
2531 c
->stream
->feed_opened
= 0;
2533 /* wake up any waiting connections to stop waiting for feed */
2534 for(c1
= first_http_ctx
; c1
!= NULL
; c1
= c1
->next
) {
2535 if (c1
->state
== HTTPSTATE_WAIT_FEED
&&
2536 c1
->stream
->feed
== c
->stream
->feed
)
2537 c1
->state
= HTTPSTATE_SEND_DATA_TRAILER
;
2542 /********************************************************************/
2545 static void rtsp_reply_header(HTTPContext
*c
, enum RTSPStatusCode error_number
)
2552 switch(error_number
) {
2553 case RTSP_STATUS_OK
:
2556 case RTSP_STATUS_METHOD
:
2557 str
= "Method Not Allowed";
2559 case RTSP_STATUS_BANDWIDTH
:
2560 str
= "Not Enough Bandwidth";
2562 case RTSP_STATUS_SESSION
:
2563 str
= "Session Not Found";
2565 case RTSP_STATUS_STATE
:
2566 str
= "Method Not Valid in This State";
2568 case RTSP_STATUS_AGGREGATE
:
2569 str
= "Aggregate operation not allowed";
2571 case RTSP_STATUS_ONLY_AGGREGATE
:
2572 str
= "Only aggregate operation allowed";
2574 case RTSP_STATUS_TRANSPORT
:
2575 str
= "Unsupported transport";
2577 case RTSP_STATUS_INTERNAL
:
2578 str
= "Internal Server Error";
2580 case RTSP_STATUS_SERVICE
:
2581 str
= "Service Unavailable";
2583 case RTSP_STATUS_VERSION
:
2584 str
= "RTSP Version not supported";
2587 str
= "Unknown Error";
2591 url_fprintf(c
->pb
, "RTSP/1.0 %d %s\r\n", error_number
, str
);
2592 url_fprintf(c
->pb
, "CSeq: %d\r\n", c
->seq
);
2594 /* output GMT time */
2598 p
= buf2
+ strlen(p
) - 1;
2601 url_fprintf(c
->pb
, "Date: %s GMT\r\n", buf2
);
2604 static void rtsp_reply_error(HTTPContext
*c
, enum RTSPStatusCode error_number
)
2606 rtsp_reply_header(c
, error_number
);
2607 url_fprintf(c
->pb
, "\r\n");
2610 static int rtsp_parse_request(HTTPContext
*c
)
2612 const char *p
, *p1
, *p2
;
2618 RTSPHeader header1
, *header
= &header1
;
2620 c
->buffer_ptr
[0] = '\0';
2623 get_word(cmd
, sizeof(cmd
), &p
);
2624 get_word(url
, sizeof(url
), &p
);
2625 get_word(protocol
, sizeof(protocol
), &p
);
2627 av_strlcpy(c
->method
, cmd
, sizeof(c
->method
));
2628 av_strlcpy(c
->url
, url
, sizeof(c
->url
));
2629 av_strlcpy(c
->protocol
, protocol
, sizeof(c
->protocol
));
2631 if (url_open_dyn_buf(&c
->pb
) < 0) {
2632 /* XXX: cannot do more */
2633 c
->pb
= NULL
; /* safety */
2637 /* check version name */
2638 if (strcmp(protocol
, "RTSP/1.0") != 0) {
2639 rtsp_reply_error(c
, RTSP_STATUS_VERSION
);
2643 /* parse each header line */
2644 memset(header
, 0, sizeof(RTSPHeader
));
2645 /* skip to next line */
2646 while (*p
!= '\n' && *p
!= '\0')
2650 while (*p
!= '\0') {
2651 p1
= strchr(p
, '\n');
2655 if (p2
> p
&& p2
[-1] == '\r')
2657 /* skip empty line */
2661 if (len
> sizeof(line
) - 1)
2662 len
= sizeof(line
) - 1;
2663 memcpy(line
, p
, len
);
2665 rtsp_parse_line(header
, line
);
2669 /* handle sequence number */
2670 c
->seq
= header
->seq
;
2672 if (!strcmp(cmd
, "DESCRIBE"))
2673 rtsp_cmd_describe(c
, url
);
2674 else if (!strcmp(cmd
, "OPTIONS"))
2675 rtsp_cmd_options(c
, url
);
2676 else if (!strcmp(cmd
, "SETUP"))
2677 rtsp_cmd_setup(c
, url
, header
);
2678 else if (!strcmp(cmd
, "PLAY"))
2679 rtsp_cmd_play(c
, url
, header
);
2680 else if (!strcmp(cmd
, "PAUSE"))
2681 rtsp_cmd_pause(c
, url
, header
);
2682 else if (!strcmp(cmd
, "TEARDOWN"))
2683 rtsp_cmd_teardown(c
, url
, header
);
2685 rtsp_reply_error(c
, RTSP_STATUS_METHOD
);
2688 len
= url_close_dyn_buf(c
->pb
, &c
->pb_buffer
);
2689 c
->pb
= NULL
; /* safety */
2691 /* XXX: cannot do more */
2694 c
->buffer_ptr
= c
->pb_buffer
;
2695 c
->buffer_end
= c
->pb_buffer
+ len
;
2696 c
->state
= RTSPSTATE_SEND_REPLY
;
2700 static int prepare_sdp_description(FFStream
*stream
, uint8_t **pbuffer
,
2701 struct in_addr my_ip
)
2703 AVFormatContext
*avc
;
2704 AVStream avs
[MAX_STREAMS
];
2707 avc
= av_alloc_format_context();
2711 if (stream
->title
[0] != 0) {
2712 av_strlcpy(avc
->title
, stream
->title
, sizeof(avc
->title
));
2714 av_strlcpy(avc
->title
, "No Title", sizeof(avc
->title
));
2716 avc
->nb_streams
= stream
->nb_streams
;
2717 if (stream
->is_multicast
) {
2718 snprintf(avc
->filename
, 1024, "rtp://%s:%d?multicast=1?ttl=%d",
2719 inet_ntoa(stream
->multicast_ip
),
2720 stream
->multicast_port
, stream
->multicast_ttl
);
2723 for(i
= 0; i
< stream
->nb_streams
; i
++) {
2724 avc
->streams
[i
] = &avs
[i
];
2725 avc
->streams
[i
]->codec
= stream
->streams
[i
]->codec
;
2727 *pbuffer
= av_mallocz(2048);
2728 avf_sdp_create(&avc
, 1, *pbuffer
, 2048);
2731 return strlen(*pbuffer
);
2734 static void rtsp_cmd_options(HTTPContext
*c
, const char *url
)
2736 // rtsp_reply_header(c, RTSP_STATUS_OK);
2737 url_fprintf(c
->pb
, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK
, "OK");
2738 url_fprintf(c
->pb
, "CSeq: %d\r\n", c
->seq
);
2739 url_fprintf(c
->pb
, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
2740 url_fprintf(c
->pb
, "\r\n");
2743 static void rtsp_cmd_describe(HTTPContext
*c
, const char *url
)
2749 int content_length
, len
;
2750 struct sockaddr_in my_addr
;
2752 /* find which url is asked */
2753 url_split(NULL
, 0, NULL
, 0, NULL
, 0, NULL
, path1
, sizeof(path1
), url
);
2758 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
2759 if (!stream
->is_feed
&&
2760 stream
->fmt
&& !strcmp(stream
->fmt
->name
, "rtp") &&
2761 !strcmp(path
, stream
->filename
)) {
2765 /* no stream found */
2766 rtsp_reply_error(c
, RTSP_STATUS_SERVICE
); /* XXX: right error ? */
2770 /* prepare the media description in sdp format */
2772 /* get the host IP */
2773 len
= sizeof(my_addr
);
2774 getsockname(c
->fd
, (struct sockaddr
*)&my_addr
, &len
);
2775 content_length
= prepare_sdp_description(stream
, &content
, my_addr
.sin_addr
);
2776 if (content_length
< 0) {
2777 rtsp_reply_error(c
, RTSP_STATUS_INTERNAL
);
2780 rtsp_reply_header(c
, RTSP_STATUS_OK
);
2781 url_fprintf(c
->pb
, "Content-Type: application/sdp\r\n");
2782 url_fprintf(c
->pb
, "Content-Length: %d\r\n", content_length
);
2783 url_fprintf(c
->pb
, "\r\n");
2784 put_buffer(c
->pb
, content
, content_length
);
2787 static HTTPContext
*find_rtp_session(const char *session_id
)
2791 if (session_id
[0] == '\0')
2794 for(c
= first_http_ctx
; c
!= NULL
; c
= c
->next
) {
2795 if (!strcmp(c
->session_id
, session_id
))
2801 static RTSPTransportField
*find_transport(RTSPHeader
*h
, enum RTSPLowerTransport lower_transport
)
2803 RTSPTransportField
*th
;
2806 for(i
=0;i
<h
->nb_transports
;i
++) {
2807 th
= &h
->transports
[i
];
2808 if (th
->lower_transport
== lower_transport
)
2814 static void rtsp_cmd_setup(HTTPContext
*c
, const char *url
,
2818 int stream_index
, port
;
2823 RTSPTransportField
*th
;
2824 struct sockaddr_in dest_addr
;
2825 RTSPActionServerSetup setup
;
2827 /* find which url is asked */
2828 url_split(NULL
, 0, NULL
, 0, NULL
, 0, NULL
, path1
, sizeof(path1
), url
);
2833 /* now check each stream */
2834 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
2835 if (!stream
->is_feed
&&
2836 stream
->fmt
&& !strcmp(stream
->fmt
->name
, "rtp")) {
2837 /* accept aggregate filenames only if single stream */
2838 if (!strcmp(path
, stream
->filename
)) {
2839 if (stream
->nb_streams
!= 1) {
2840 rtsp_reply_error(c
, RTSP_STATUS_AGGREGATE
);
2847 for(stream_index
= 0; stream_index
< stream
->nb_streams
;
2849 snprintf(buf
, sizeof(buf
), "%s/streamid=%d",
2850 stream
->filename
, stream_index
);
2851 if (!strcmp(path
, buf
))
2856 /* no stream found */
2857 rtsp_reply_error(c
, RTSP_STATUS_SERVICE
); /* XXX: right error ? */
2861 /* generate session id if needed */
2862 if (h
->session_id
[0] == '\0')
2863 snprintf(h
->session_id
, sizeof(h
->session_id
), "%08x%08x",
2864 av_random(&random_state
), av_random(&random_state
));
2866 /* find rtp session, and create it if none found */
2867 rtp_c
= find_rtp_session(h
->session_id
);
2869 /* always prefer UDP */
2870 th
= find_transport(h
, RTSP_LOWER_TRANSPORT_UDP
);
2872 th
= find_transport(h
, RTSP_LOWER_TRANSPORT_TCP
);
2874 rtsp_reply_error(c
, RTSP_STATUS_TRANSPORT
);
2879 rtp_c
= rtp_new_connection(&c
->from_addr
, stream
, h
->session_id
,
2880 th
->lower_transport
);
2882 rtsp_reply_error(c
, RTSP_STATUS_BANDWIDTH
);
2886 /* open input stream */
2887 if (open_input_stream(rtp_c
, "") < 0) {
2888 rtsp_reply_error(c
, RTSP_STATUS_INTERNAL
);
2893 /* test if stream is OK (test needed because several SETUP needs
2894 to be done for a given file) */
2895 if (rtp_c
->stream
!= stream
) {
2896 rtsp_reply_error(c
, RTSP_STATUS_SERVICE
);
2900 /* test if stream is already set up */
2901 if (rtp_c
->rtp_ctx
[stream_index
]) {
2902 rtsp_reply_error(c
, RTSP_STATUS_STATE
);
2906 /* check transport */
2907 th
= find_transport(h
, rtp_c
->rtp_protocol
);
2908 if (!th
|| (th
->lower_transport
== RTSP_LOWER_TRANSPORT_UDP
&&
2909 th
->client_port_min
<= 0)) {
2910 rtsp_reply_error(c
, RTSP_STATUS_TRANSPORT
);
2914 /* setup default options */
2915 setup
.transport_option
[0] = '\0';
2916 dest_addr
= rtp_c
->from_addr
;
2917 dest_addr
.sin_port
= htons(th
->client_port_min
);
2920 if (rtp_new_av_stream(rtp_c
, stream_index
, &dest_addr
, c
) < 0) {
2921 rtsp_reply_error(c
, RTSP_STATUS_TRANSPORT
);
2925 /* now everything is OK, so we can send the connection parameters */
2926 rtsp_reply_header(c
, RTSP_STATUS_OK
);
2928 url_fprintf(c
->pb
, "Session: %s\r\n", rtp_c
->session_id
);
2930 switch(rtp_c
->rtp_protocol
) {
2931 case RTSP_LOWER_TRANSPORT_UDP
:
2932 port
= rtp_get_local_port(rtp_c
->rtp_handles
[stream_index
]);
2933 url_fprintf(c
->pb
, "Transport: RTP/AVP/UDP;unicast;"
2934 "client_port=%d-%d;server_port=%d-%d",
2935 th
->client_port_min
, th
->client_port_min
+ 1,
2938 case RTSP_LOWER_TRANSPORT_TCP
:
2939 url_fprintf(c
->pb
, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
2940 stream_index
* 2, stream_index
* 2 + 1);
2945 if (setup
.transport_option
[0] != '\0')
2946 url_fprintf(c
->pb
, ";%s", setup
.transport_option
);
2947 url_fprintf(c
->pb
, "\r\n");
2950 url_fprintf(c
->pb
, "\r\n");
2954 /* find an rtp connection by using the session ID. Check consistency
2956 static HTTPContext
*find_rtp_session_with_url(const char *url
,
2957 const char *session_id
)
2965 rtp_c
= find_rtp_session(session_id
);
2969 /* find which url is asked */
2970 url_split(NULL
, 0, NULL
, 0, NULL
, 0, NULL
, path1
, sizeof(path1
), url
);
2974 if(!strcmp(path
, rtp_c
->stream
->filename
)) return rtp_c
;
2975 for(s
=0; s
<rtp_c
->stream
->nb_streams
; ++s
) {
2976 snprintf(buf
, sizeof(buf
), "%s/streamid=%d",
2977 rtp_c
->stream
->filename
, s
);
2978 if(!strncmp(path
, buf
, sizeof(buf
))) {
2979 // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
2986 static void rtsp_cmd_play(HTTPContext
*c
, const char *url
, RTSPHeader
*h
)
2990 rtp_c
= find_rtp_session_with_url(url
, h
->session_id
);
2992 rtsp_reply_error(c
, RTSP_STATUS_SESSION
);
2996 if (rtp_c
->state
!= HTTPSTATE_SEND_DATA
&&
2997 rtp_c
->state
!= HTTPSTATE_WAIT_FEED
&&
2998 rtp_c
->state
!= HTTPSTATE_READY
) {
2999 rtsp_reply_error(c
, RTSP_STATUS_STATE
);
3004 /* XXX: seek in stream */
3005 if (h
->range_start
!= AV_NOPTS_VALUE
) {
3006 printf("range_start=%0.3f\n", (double)h
->range_start
/ AV_TIME_BASE
);
3007 av_seek_frame(rtp_c
->fmt_in
, -1, h
->range_start
);
3011 rtp_c
->state
= HTTPSTATE_SEND_DATA
;
3013 /* now everything is OK, so we can send the connection parameters */
3014 rtsp_reply_header(c
, RTSP_STATUS_OK
);
3016 url_fprintf(c
->pb
, "Session: %s\r\n", rtp_c
->session_id
);
3017 url_fprintf(c
->pb
, "\r\n");
3020 static void rtsp_cmd_pause(HTTPContext
*c
, const char *url
, RTSPHeader
*h
)
3024 rtp_c
= find_rtp_session_with_url(url
, h
->session_id
);
3026 rtsp_reply_error(c
, RTSP_STATUS_SESSION
);
3030 if (rtp_c
->state
!= HTTPSTATE_SEND_DATA
&&
3031 rtp_c
->state
!= HTTPSTATE_WAIT_FEED
) {
3032 rtsp_reply_error(c
, RTSP_STATUS_STATE
);
3036 rtp_c
->state
= HTTPSTATE_READY
;
3037 rtp_c
->first_pts
= AV_NOPTS_VALUE
;
3038 /* now everything is OK, so we can send the connection parameters */
3039 rtsp_reply_header(c
, RTSP_STATUS_OK
);
3041 url_fprintf(c
->pb
, "Session: %s\r\n", rtp_c
->session_id
);
3042 url_fprintf(c
->pb
, "\r\n");
3045 static void rtsp_cmd_teardown(HTTPContext
*c
, const char *url
, RTSPHeader
*h
)
3048 char session_id
[32];
3050 rtp_c
= find_rtp_session_with_url(url
, h
->session_id
);
3052 rtsp_reply_error(c
, RTSP_STATUS_SESSION
);
3056 av_strlcpy(session_id
, rtp_c
->session_id
, sizeof(session_id
));
3058 /* abort the session */
3059 close_connection(rtp_c
);
3061 /* now everything is OK, so we can send the connection parameters */
3062 rtsp_reply_header(c
, RTSP_STATUS_OK
);
3064 url_fprintf(c
->pb
, "Session: %s\r\n", session_id
);
3065 url_fprintf(c
->pb
, "\r\n");
3069 /********************************************************************/
3072 static HTTPContext
*rtp_new_connection(struct sockaddr_in
*from_addr
,
3073 FFStream
*stream
, const char *session_id
,
3074 enum RTSPLowerTransport rtp_protocol
)
3076 HTTPContext
*c
= NULL
;
3077 const char *proto_str
;
3079 /* XXX: should output a warning page when coming
3080 close to the connection limit */
3081 if (nb_connections
>= nb_max_connections
)
3084 /* add a new connection */
3085 c
= av_mallocz(sizeof(HTTPContext
));
3090 c
->poll_entry
= NULL
;
3091 c
->from_addr
= *from_addr
;
3092 c
->buffer_size
= IOBUFFER_INIT_SIZE
;
3093 c
->buffer
= av_malloc(c
->buffer_size
);
3098 av_strlcpy(c
->session_id
, session_id
, sizeof(c
->session_id
));
3099 c
->state
= HTTPSTATE_READY
;
3100 c
->is_packetized
= 1;
3101 c
->rtp_protocol
= rtp_protocol
;
3103 /* protocol is shown in statistics */
3104 switch(c
->rtp_protocol
) {
3105 case RTSP_LOWER_TRANSPORT_UDP_MULTICAST
:
3106 proto_str
= "MCAST";
3108 case RTSP_LOWER_TRANSPORT_UDP
:
3111 case RTSP_LOWER_TRANSPORT_TCP
:
3118 av_strlcpy(c
->protocol
, "RTP/", sizeof(c
->protocol
));
3119 av_strlcat(c
->protocol
, proto_str
, sizeof(c
->protocol
));
3121 current_bandwidth
+= stream
->bandwidth
;
3123 c
->next
= first_http_ctx
;
3135 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3136 command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
3138 static int rtp_new_av_stream(HTTPContext
*c
,
3139 int stream_index
, struct sockaddr_in
*dest_addr
,
3140 HTTPContext
*rtsp_c
)
3142 AVFormatContext
*ctx
;
3145 URLContext
*h
= NULL
;
3147 int max_packet_size
;
3149 /* now we can open the relevant output stream */
3150 ctx
= av_alloc_format_context();
3153 ctx
->oformat
= guess_format("rtp", NULL
, NULL
);
3155 st
= av_mallocz(sizeof(AVStream
));
3158 st
->codec
= avcodec_alloc_context();
3159 ctx
->nb_streams
= 1;
3160 ctx
->streams
[0] = st
;
3162 if (!c
->stream
->feed
||
3163 c
->stream
->feed
== c
->stream
)
3164 memcpy(st
, c
->stream
->streams
[stream_index
], sizeof(AVStream
));
3167 c
->stream
->feed
->streams
[c
->stream
->feed_streams
[stream_index
]],
3169 st
->priv_data
= NULL
;
3171 /* build destination RTP address */
3172 ipaddr
= inet_ntoa(dest_addr
->sin_addr
);
3174 switch(c
->rtp_protocol
) {
3175 case RTSP_LOWER_TRANSPORT_UDP
:
3176 case RTSP_LOWER_TRANSPORT_UDP_MULTICAST
:
3179 /* XXX: also pass as parameter to function ? */
3180 if (c
->stream
->is_multicast
) {
3182 ttl
= c
->stream
->multicast_ttl
;
3185 snprintf(ctx
->filename
, sizeof(ctx
->filename
),
3186 "rtp://%s:%d?multicast=1&ttl=%d",
3187 ipaddr
, ntohs(dest_addr
->sin_port
), ttl
);
3189 snprintf(ctx
->filename
, sizeof(ctx
->filename
),
3190 "rtp://%s:%d", ipaddr
, ntohs(dest_addr
->sin_port
));
3193 if (url_open(&h
, ctx
->filename
, URL_WRONLY
) < 0)
3195 c
->rtp_handles
[stream_index
] = h
;
3196 max_packet_size
= url_get_max_packet_size(h
);
3198 case RTSP_LOWER_TRANSPORT_TCP
:
3201 max_packet_size
= RTSP_TCP_MAX_PACKET_SIZE
;
3207 http_log("%s:%d - - \"PLAY %s/streamid=%d %s\"\n",
3208 ipaddr
, ntohs(dest_addr
->sin_port
),
3209 c
->stream
->filename
, stream_index
, c
->protocol
);
3211 /* normally, no packets should be output here, but the packet size may be checked */
3212 if (url_open_dyn_packet_buf(&ctx
->pb
, max_packet_size
) < 0) {
3213 /* XXX: close stream */
3216 av_set_parameters(ctx
, NULL
);
3217 if (av_write_header(ctx
) < 0) {
3224 url_close_dyn_buf(ctx
->pb
, &dummy_buf
);
3227 c
->rtp_ctx
[stream_index
] = ctx
;
3231 /********************************************************************/
3232 /* ffserver initialization */
3234 static AVStream
*add_av_stream1(FFStream
*stream
, AVCodecContext
*codec
)
3238 fst
= av_mallocz(sizeof(AVStream
));
3241 fst
->codec
= avcodec_alloc_context();
3242 fst
->priv_data
= av_mallocz(sizeof(FeedData
));
3243 memcpy(fst
->codec
, codec
, sizeof(AVCodecContext
));
3244 fst
->index
= stream
->nb_streams
;
3245 av_set_pts_info(fst
, 33, 1, 90000);
3246 stream
->streams
[stream
->nb_streams
++] = fst
;
3250 /* return the stream number in the feed */
3251 static int add_av_stream(FFStream
*feed
, AVStream
*st
)
3254 AVCodecContext
*av
, *av1
;
3258 for(i
=0;i
<feed
->nb_streams
;i
++) {
3259 st
= feed
->streams
[i
];
3261 if (av1
->codec_id
== av
->codec_id
&&
3262 av1
->codec_type
== av
->codec_type
&&
3263 av1
->bit_rate
== av
->bit_rate
) {
3265 switch(av
->codec_type
) {
3266 case CODEC_TYPE_AUDIO
:
3267 if (av1
->channels
== av
->channels
&&
3268 av1
->sample_rate
== av
->sample_rate
)
3271 case CODEC_TYPE_VIDEO
:
3272 if (av1
->width
== av
->width
&&
3273 av1
->height
== av
->height
&&
3274 av1
->time_base
.den
== av
->time_base
.den
&&
3275 av1
->time_base
.num
== av
->time_base
.num
&&
3276 av1
->gop_size
== av
->gop_size
)
3285 fst
= add_av_stream1(feed
, av
);
3288 return feed
->nb_streams
- 1;
3293 static void remove_stream(FFStream
*stream
)
3297 while (*ps
!= NULL
) {
3305 /* specific mpeg4 handling : we extract the raw parameters */
3306 static void extract_mpeg4_header(AVFormatContext
*infile
)
3308 int mpeg4_count
, i
, size
;
3314 for(i
=0;i
<infile
->nb_streams
;i
++) {
3315 st
= infile
->streams
[i
];
3316 if (st
->codec
->codec_id
== CODEC_ID_MPEG4
&&
3317 st
->codec
->extradata_size
== 0) {
3324 printf("MPEG4 without extra data: trying to find header in %s\n", infile
->filename
);
3325 while (mpeg4_count
> 0) {
3326 if (av_read_packet(infile
, &pkt
) < 0)
3328 st
= infile
->streams
[pkt
.stream_index
];
3329 if (st
->codec
->codec_id
== CODEC_ID_MPEG4
&&
3330 st
->codec
->extradata_size
== 0) {
3331 av_freep(&st
->codec
->extradata
);
3332 /* fill extradata with the header */
3333 /* XXX: we make hard suppositions here ! */
3335 while (p
< pkt
.data
+ pkt
.size
- 4) {
3336 /* stop when vop header is found */
3337 if (p
[0] == 0x00 && p
[1] == 0x00 &&
3338 p
[2] == 0x01 && p
[3] == 0xb6) {
3339 size
= p
- pkt
.data
;
3340 // av_hex_dump_log(infile, AV_LOG_DEBUG, pkt.data, size);
3341 st
->codec
->extradata
= av_malloc(size
);
3342 st
->codec
->extradata_size
= size
;
3343 memcpy(st
->codec
->extradata
, pkt
.data
, size
);
3350 av_free_packet(&pkt
);
3354 /* compute the needed AVStream for each file */
3355 static void build_file_streams(void)
3357 FFStream
*stream
, *stream_next
;
3358 AVFormatContext
*infile
;
3361 /* gather all streams */
3362 for(stream
= first_stream
; stream
!= NULL
; stream
= stream_next
) {
3363 stream_next
= stream
->next
;
3364 if (stream
->stream_type
== STREAM_TYPE_LIVE
&&
3366 /* the stream comes from a file */
3367 /* try to open the file */
3369 stream
->ap_in
= av_mallocz(sizeof(AVFormatParameters
));
3370 if (stream
->fmt
&& !strcmp(stream
->fmt
->name
, "rtp")) {
3371 /* specific case : if transport stream output to RTP,
3372 we use a raw transport stream reader */
3373 stream
->ap_in
->mpeg2ts_raw
= 1;
3374 stream
->ap_in
->mpeg2ts_compute_pcr
= 1;
3377 if ((ret
= av_open_input_file(&infile
, stream
->feed_filename
,
3378 stream
->ifmt
, 0, stream
->ap_in
)) < 0) {
3379 http_log("could not open %s: %d\n", stream
->feed_filename
, ret
);
3380 /* remove stream (no need to spend more time on it) */
3382 remove_stream(stream
);
3384 /* find all the AVStreams inside and reference them in
3386 if (av_find_stream_info(infile
) < 0) {
3387 http_log("Could not find codec parameters from '%s'\n",
3388 stream
->feed_filename
);
3389 av_close_input_file(infile
);
3392 extract_mpeg4_header(infile
);
3394 for(i
=0;i
<infile
->nb_streams
;i
++)
3395 add_av_stream1(stream
, infile
->streams
[i
]->codec
);
3397 av_close_input_file(infile
);
3403 /* compute the needed AVStream for each feed */
3404 static void build_feed_streams(void)
3406 FFStream
*stream
, *feed
;
3409 /* gather all streams */
3410 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
3411 feed
= stream
->feed
;
3413 if (!stream
->is_feed
) {
3414 /* we handle a stream coming from a feed */
3415 for(i
=0;i
<stream
->nb_streams
;i
++)
3416 stream
->feed_streams
[i
] = add_av_stream(feed
, stream
->streams
[i
]);
3421 /* gather all streams */
3422 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
3423 feed
= stream
->feed
;
3425 if (stream
->is_feed
) {
3426 for(i
=0;i
<stream
->nb_streams
;i
++)
3427 stream
->feed_streams
[i
] = i
;
3432 /* create feed files if needed */
3433 for(feed
= first_feed
; feed
!= NULL
; feed
= feed
->next_feed
) {
3436 if (url_exist(feed
->feed_filename
)) {
3437 /* See if it matches */
3441 if (av_open_input_file(&s
, feed
->feed_filename
, NULL
, FFM_PACKET_SIZE
, NULL
) >= 0) {
3442 /* Now see if it matches */
3443 if (s
->nb_streams
== feed
->nb_streams
) {
3445 for(i
=0;i
<s
->nb_streams
;i
++) {
3447 sf
= feed
->streams
[i
];
3450 if (sf
->index
!= ss
->index
||
3452 http_log("Index & Id do not match for stream %d (%s)\n",
3453 i
, feed
->feed_filename
);
3456 AVCodecContext
*ccf
, *ccs
;
3460 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3462 if (CHECK_CODEC(codec
) || CHECK_CODEC(codec_type
)) {
3463 http_log("Codecs do not match for stream %d\n", i
);
3465 } else if (CHECK_CODEC(bit_rate
) || CHECK_CODEC(flags
)) {
3466 http_log("Codec bitrates do not match for stream %d\n", i
);
3468 } else if (ccf
->codec_type
== CODEC_TYPE_VIDEO
) {
3469 if (CHECK_CODEC(time_base
.den
) ||
3470 CHECK_CODEC(time_base
.num
) ||
3471 CHECK_CODEC(width
) ||
3472 CHECK_CODEC(height
)) {
3473 http_log("Codec width, height and framerate do not match for stream %d\n", i
);
3476 } else if (ccf
->codec_type
== CODEC_TYPE_AUDIO
) {
3477 if (CHECK_CODEC(sample_rate
) ||
3478 CHECK_CODEC(channels
) ||
3479 CHECK_CODEC(frame_size
)) {
3480 http_log("Codec sample_rate, channels, frame_size do not match for stream %d\n", i
);
3484 http_log("Unknown codec type\n");
3492 http_log("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3493 feed
->feed_filename
, s
->nb_streams
, feed
->nb_streams
);
3495 av_close_input_file(s
);
3497 http_log("Deleting feed file '%s' as it appears to be corrupt\n",
3498 feed
->feed_filename
);
3501 if (feed
->readonly
) {
3502 http_log("Unable to delete feed file '%s' as it is marked readonly\n",
3503 feed
->feed_filename
);
3506 unlink(feed
->feed_filename
);
3509 if (!url_exist(feed
->feed_filename
)) {
3510 AVFormatContext s1
, *s
= &s1
;
3512 if (feed
->readonly
) {
3513 http_log("Unable to create feed file '%s' as it is marked readonly\n",
3514 feed
->feed_filename
);
3518 /* only write the header of the ffm file */
3519 if (url_fopen(&s
->pb
, feed
->feed_filename
, URL_WRONLY
) < 0) {
3520 http_log("Could not open output feed file '%s'\n",
3521 feed
->feed_filename
);
3524 s
->oformat
= feed
->fmt
;
3525 s
->nb_streams
= feed
->nb_streams
;
3526 for(i
=0;i
<s
->nb_streams
;i
++) {
3528 st
= feed
->streams
[i
];
3531 av_set_parameters(s
, NULL
);
3532 if (av_write_header(s
) < 0) {
3533 http_log("Container doesn't supports the required parameters\n");
3536 /* XXX: need better api */
3537 av_freep(&s
->priv_data
);
3540 /* get feed size and write index */
3541 fd
= open(feed
->feed_filename
, O_RDONLY
);
3543 http_log("Could not open output feed file '%s'\n",
3544 feed
->feed_filename
);
3548 feed
->feed_write_index
= ffm_read_write_index(fd
);
3549 feed
->feed_size
= lseek(fd
, 0, SEEK_END
);
3550 /* ensure that we do not wrap before the end of file */
3551 if (feed
->feed_max_size
&& feed
->feed_max_size
< feed
->feed_size
)
3552 feed
->feed_max_size
= feed
->feed_size
;
3558 /* compute the bandwidth used by each stream */
3559 static void compute_bandwidth(void)
3565 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
3567 for(i
=0;i
<stream
->nb_streams
;i
++) {
3568 AVStream
*st
= stream
->streams
[i
];
3569 switch(st
->codec
->codec_type
) {
3570 case CODEC_TYPE_AUDIO
:
3571 case CODEC_TYPE_VIDEO
:
3572 bandwidth
+= st
->codec
->bit_rate
;
3578 stream
->bandwidth
= (bandwidth
+ 999) / 1000;
3582 static void get_arg(char *buf
, int buf_size
, const char **pp
)
3589 while (isspace(*p
)) p
++;
3592 if (*p
== '\"' || *p
== '\'')
3604 if ((q
- buf
) < buf_size
- 1)
3609 if (quote
&& *p
== quote
)
3614 /* add a codec and set the default parameters */
3615 static void add_codec(FFStream
*stream
, AVCodecContext
*av
)
3619 /* compute default parameters */
3620 switch(av
->codec_type
) {
3621 case CODEC_TYPE_AUDIO
:
3622 if (av
->bit_rate
== 0)
3623 av
->bit_rate
= 64000;
3624 if (av
->sample_rate
== 0)
3625 av
->sample_rate
= 22050;
3626 if (av
->channels
== 0)
3629 case CODEC_TYPE_VIDEO
:
3630 if (av
->bit_rate
== 0)
3631 av
->bit_rate
= 64000;
3632 if (av
->time_base
.num
== 0){
3633 av
->time_base
.den
= 5;
3634 av
->time_base
.num
= 1;
3636 if (av
->width
== 0 || av
->height
== 0) {
3640 /* Bitrate tolerance is less for streaming */
3641 if (av
->bit_rate_tolerance
== 0)
3642 av
->bit_rate_tolerance
= FFMAX(av
->bit_rate
/ 4,
3643 (int64_t)av
->bit_rate
*av
->time_base
.num
/av
->time_base
.den
);
3648 if (av
->max_qdiff
== 0)
3650 av
->qcompress
= 0.5;
3653 if (!av
->nsse_weight
)
3654 av
->nsse_weight
= 8;
3656 av
->frame_skip_cmp
= FF_CMP_DCTMAX
;
3657 av
->me_method
= ME_EPZS
;
3658 av
->rc_buffer_aggressivity
= 1.0;
3661 av
->rc_eq
= "tex^qComp";
3662 if (!av
->i_quant_factor
)
3663 av
->i_quant_factor
= -0.8;
3664 if (!av
->b_quant_factor
)
3665 av
->b_quant_factor
= 1.25;
3666 if (!av
->b_quant_offset
)
3667 av
->b_quant_offset
= 1.25;
3668 if (!av
->rc_max_rate
)
3669 av
->rc_max_rate
= av
->bit_rate
* 2;
3671 if (av
->rc_max_rate
&& !av
->rc_buffer_size
) {
3672 av
->rc_buffer_size
= av
->rc_max_rate
;
3681 st
= av_mallocz(sizeof(AVStream
));
3684 st
->codec
= avcodec_alloc_context();
3685 stream
->streams
[stream
->nb_streams
++] = st
;
3686 memcpy(st
->codec
, av
, sizeof(AVCodecContext
));
3689 static int opt_audio_codec(const char *arg
)
3691 AVCodec
*p
= avcodec_find_encoder_by_name(arg
);
3693 if (p
== NULL
|| p
->type
!= CODEC_TYPE_AUDIO
)
3694 return CODEC_ID_NONE
;
3699 static int opt_video_codec(const char *arg
)
3701 AVCodec
*p
= avcodec_find_encoder_by_name(arg
);
3703 if (p
== NULL
|| p
->type
!= CODEC_TYPE_VIDEO
)
3704 return CODEC_ID_NONE
;
3709 /* simplistic plugin support */
3712 static void load_module(const char *filename
)
3715 void (*init_func
)(void);
3716 dll
= dlopen(filename
, RTLD_NOW
);
3718 fprintf(stderr
, "Could not load module '%s' - %s\n",
3719 filename
, dlerror());
3723 init_func
= dlsym(dll
, "ffserver_module_init");
3726 "%s: init function 'ffserver_module_init()' not found\n",
3735 static int ffserver_opt_default(const char *opt
, const char *arg
,
3736 AVCodecContext
*avctx
, int type
)
3738 const AVOption
*o
= NULL
;
3739 const AVOption
*o2
= av_find_opt(avctx
, opt
, NULL
, type
, type
);
3741 o
= av_set_string2(avctx
, opt
, arg
, 1);
3747 static int parse_ffconfig(const char *filename
)
3754 int val
, errors
, line_num
;
3755 FFStream
**last_stream
, *stream
, *redirect
;
3756 FFStream
**last_feed
, *feed
;
3757 AVCodecContext audio_enc
, video_enc
;
3758 int audio_id
, video_id
;
3760 f
= fopen(filename
, "r");
3768 first_stream
= NULL
;
3769 last_stream
= &first_stream
;
3771 last_feed
= &first_feed
;
3775 audio_id
= CODEC_ID_NONE
;
3776 video_id
= CODEC_ID_NONE
;
3778 if (fgets(line
, sizeof(line
), f
) == NULL
)
3784 if (*p
== '\0' || *p
== '#')
3787 get_arg(cmd
, sizeof(cmd
), &p
);
3789 if (!strcasecmp(cmd
, "Port")) {
3790 get_arg(arg
, sizeof(arg
), &p
);
3792 if (val
< 1 || val
> 65536) {
3793 fprintf(stderr
, "%s:%d: Invalid port: %s\n",
3794 filename
, line_num
, arg
);
3797 my_http_addr
.sin_port
= htons(val
);
3798 } else if (!strcasecmp(cmd
, "BindAddress")) {
3799 get_arg(arg
, sizeof(arg
), &p
);
3800 if (resolve_host(&my_http_addr
.sin_addr
, arg
) != 0) {
3801 fprintf(stderr
, "%s:%d: Invalid host/IP address: %s\n",
3802 filename
, line_num
, arg
);
3805 } else if (!strcasecmp(cmd
, "NoDaemon")) {
3806 ffserver_daemon
= 0;
3807 } else if (!strcasecmp(cmd
, "RTSPPort")) {
3808 get_arg(arg
, sizeof(arg
), &p
);
3810 if (val
< 1 || val
> 65536) {
3811 fprintf(stderr
, "%s:%d: Invalid port: %s\n",
3812 filename
, line_num
, arg
);
3815 my_rtsp_addr
.sin_port
= htons(atoi(arg
));
3816 } else if (!strcasecmp(cmd
, "RTSPBindAddress")) {
3817 get_arg(arg
, sizeof(arg
), &p
);
3818 if (resolve_host(&my_rtsp_addr
.sin_addr
, arg
) != 0) {
3819 fprintf(stderr
, "%s:%d: Invalid host/IP address: %s\n",
3820 filename
, line_num
, arg
);
3823 } else if (!strcasecmp(cmd
, "MaxHTTPConnections")) {
3824 get_arg(arg
, sizeof(arg
), &p
);
3826 if (val
< 1 || val
> 65536) {
3827 fprintf(stderr
, "%s:%d: Invalid MaxHTTPConnections: %s\n",
3828 filename
, line_num
, arg
);
3831 nb_max_http_connections
= val
;
3832 } else if (!strcasecmp(cmd
, "MaxClients")) {
3833 get_arg(arg
, sizeof(arg
), &p
);
3835 if (val
< 1 || val
> nb_max_http_connections
) {
3836 fprintf(stderr
, "%s:%d: Invalid MaxClients: %s\n",
3837 filename
, line_num
, arg
);
3840 nb_max_connections
= val
;
3842 } else if (!strcasecmp(cmd
, "MaxBandwidth")) {
3844 get_arg(arg
, sizeof(arg
), &p
);
3846 if (llval
< 10 || llval
> 10000000) {
3847 fprintf(stderr
, "%s:%d: Invalid MaxBandwidth: %s\n",
3848 filename
, line_num
, arg
);
3851 max_bandwidth
= llval
;
3852 } else if (!strcasecmp(cmd
, "CustomLog")) {
3853 if (!ffserver_debug
)
3854 get_arg(logfilename
, sizeof(logfilename
), &p
);
3855 } else if (!strcasecmp(cmd
, "<Feed")) {
3856 /*********************************************/
3857 /* Feed related options */
3859 if (stream
|| feed
) {
3860 fprintf(stderr
, "%s:%d: Already in a tag\n",
3861 filename
, line_num
);
3863 feed
= av_mallocz(sizeof(FFStream
));
3864 /* add in stream list */
3865 *last_stream
= feed
;
3866 last_stream
= &feed
->next
;
3867 /* add in feed list */
3869 last_feed
= &feed
->next_feed
;
3871 get_arg(feed
->filename
, sizeof(feed
->filename
), &p
);
3872 q
= strrchr(feed
->filename
, '>');
3875 feed
->fmt
= guess_format("ffm", NULL
, NULL
);
3876 /* defaut feed file */
3877 snprintf(feed
->feed_filename
, sizeof(feed
->feed_filename
),
3878 "/tmp/%s.ffm", feed
->filename
);
3879 feed
->feed_max_size
= 5 * 1024 * 1024;
3881 feed
->feed
= feed
; /* self feeding :-) */
3883 } else if (!strcasecmp(cmd
, "Launch")) {
3887 feed
->child_argv
= av_mallocz(64 * sizeof(char *));
3889 for (i
= 0; i
< 62; i
++) {
3890 get_arg(arg
, sizeof(arg
), &p
);
3894 feed
->child_argv
[i
] = av_strdup(arg
);
3897 feed
->child_argv
[i
] = av_malloc(30 + strlen(feed
->filename
));
3899 snprintf(feed
->child_argv
[i
], 30+strlen(feed
->filename
),
3901 (my_http_addr
.sin_addr
.s_addr
== INADDR_ANY
) ? "127.0.0.1" :
3902 inet_ntoa(my_http_addr
.sin_addr
),
3903 ntohs(my_http_addr
.sin_port
), feed
->filename
);
3905 } else if (!strcasecmp(cmd
, "ReadOnlyFile")) {
3907 get_arg(feed
->feed_filename
, sizeof(feed
->feed_filename
), &p
);
3909 } else if (stream
) {
3910 get_arg(stream
->feed_filename
, sizeof(stream
->feed_filename
), &p
);
3912 } else if (!strcasecmp(cmd
, "File")) {
3914 get_arg(feed
->feed_filename
, sizeof(feed
->feed_filename
), &p
);
3916 get_arg(stream
->feed_filename
, sizeof(stream
->feed_filename
), &p
);
3917 } else if (!strcasecmp(cmd
, "FileMaxSize")) {
3922 get_arg(arg
, sizeof(arg
), &p
);
3924 fsize
= strtod(p1
, &p1
);
3925 switch(toupper(*p1
)) {
3930 fsize
*= 1024 * 1024;
3933 fsize
*= 1024 * 1024 * 1024;
3936 feed
->feed_max_size
= (int64_t)fsize
;
3938 } else if (!strcasecmp(cmd
, "</Feed>")) {
3940 fprintf(stderr
, "%s:%d: No corresponding <Feed> for </Feed>\n",
3941 filename
, line_num
);
3945 } else if (!strcasecmp(cmd
, "<Stream")) {
3946 /*********************************************/
3947 /* Stream related options */
3949 if (stream
|| feed
) {
3950 fprintf(stderr
, "%s:%d: Already in a tag\n",
3951 filename
, line_num
);
3953 const AVClass
*class;
3954 stream
= av_mallocz(sizeof(FFStream
));
3955 *last_stream
= stream
;
3956 last_stream
= &stream
->next
;
3958 get_arg(stream
->filename
, sizeof(stream
->filename
), &p
);
3959 q
= strrchr(stream
->filename
, '>');
3962 stream
->fmt
= guess_stream_format(NULL
, stream
->filename
, NULL
);
3963 /* fetch avclass so AVOption works
3964 * FIXME try to use avcodec_get_context_defaults2
3965 * without changing defaults too much */
3966 avcodec_get_context_defaults(&video_enc
);
3967 class = video_enc
.av_class
;
3968 memset(&audio_enc
, 0, sizeof(AVCodecContext
));
3969 memset(&video_enc
, 0, sizeof(AVCodecContext
));
3970 audio_enc
.av_class
= class;
3971 video_enc
.av_class
= class;
3972 audio_id
= CODEC_ID_NONE
;
3973 video_id
= CODEC_ID_NONE
;
3975 audio_id
= stream
->fmt
->audio_codec
;
3976 video_id
= stream
->fmt
->video_codec
;
3979 } else if (!strcasecmp(cmd
, "Feed")) {
3980 get_arg(arg
, sizeof(arg
), &p
);
3985 while (sfeed
!= NULL
) {
3986 if (!strcmp(sfeed
->filename
, arg
))
3988 sfeed
= sfeed
->next_feed
;
3991 fprintf(stderr
, "%s:%d: feed '%s' not defined\n",
3992 filename
, line_num
, arg
);
3994 stream
->feed
= sfeed
;
3996 } else if (!strcasecmp(cmd
, "Format")) {
3997 get_arg(arg
, sizeof(arg
), &p
);
3999 if (!strcmp(arg
, "status")) {
4000 stream
->stream_type
= STREAM_TYPE_STATUS
;
4003 stream
->stream_type
= STREAM_TYPE_LIVE
;
4004 /* jpeg cannot be used here, so use single frame jpeg */
4005 if (!strcmp(arg
, "jpeg"))
4006 strcpy(arg
, "mjpeg");
4007 stream
->fmt
= guess_stream_format(arg
, NULL
, NULL
);
4009 fprintf(stderr
, "%s:%d: Unknown Format: %s\n",
4010 filename
, line_num
, arg
);
4015 audio_id
= stream
->fmt
->audio_codec
;
4016 video_id
= stream
->fmt
->video_codec
;
4019 } else if (!strcasecmp(cmd
, "InputFormat")) {
4020 get_arg(arg
, sizeof(arg
), &p
);
4022 stream
->ifmt
= av_find_input_format(arg
);
4023 if (!stream
->ifmt
) {
4024 fprintf(stderr
, "%s:%d: Unknown input format: %s\n",
4025 filename
, line_num
, arg
);
4028 } else if (!strcasecmp(cmd
, "FaviconURL")) {
4029 if (stream
&& stream
->stream_type
== STREAM_TYPE_STATUS
) {
4030 get_arg(stream
->feed_filename
, sizeof(stream
->feed_filename
), &p
);
4032 fprintf(stderr
, "%s:%d: FaviconURL only permitted for status streams\n",
4033 filename
, line_num
);
4036 } else if (!strcasecmp(cmd
, "Author")) {
4038 get_arg(stream
->author
, sizeof(stream
->author
), &p
);
4039 } else if (!strcasecmp(cmd
, "Comment")) {
4041 get_arg(stream
->comment
, sizeof(stream
->comment
), &p
);
4042 } else if (!strcasecmp(cmd
, "Copyright")) {
4044 get_arg(stream
->copyright
, sizeof(stream
->copyright
), &p
);
4045 } else if (!strcasecmp(cmd
, "Title")) {
4047 get_arg(stream
->title
, sizeof(stream
->title
), &p
);
4048 } else if (!strcasecmp(cmd
, "Preroll")) {
4049 get_arg(arg
, sizeof(arg
), &p
);
4051 stream
->prebuffer
= atof(arg
) * 1000;
4052 } else if (!strcasecmp(cmd
, "StartSendOnKey")) {
4054 stream
->send_on_key
= 1;
4055 } else if (!strcasecmp(cmd
, "AudioCodec")) {
4056 get_arg(arg
, sizeof(arg
), &p
);
4057 audio_id
= opt_audio_codec(arg
);
4058 if (audio_id
== CODEC_ID_NONE
) {
4059 fprintf(stderr
, "%s:%d: Unknown AudioCodec: %s\n",
4060 filename
, line_num
, arg
);
4063 } else if (!strcasecmp(cmd
, "VideoCodec")) {
4064 get_arg(arg
, sizeof(arg
), &p
);
4065 video_id
= opt_video_codec(arg
);
4066 if (video_id
== CODEC_ID_NONE
) {
4067 fprintf(stderr
, "%s:%d: Unknown VideoCodec: %s\n",
4068 filename
, line_num
, arg
);
4071 } else if (!strcasecmp(cmd
, "MaxTime")) {
4072 get_arg(arg
, sizeof(arg
), &p
);
4074 stream
->max_time
= atof(arg
) * 1000;
4075 } else if (!strcasecmp(cmd
, "AudioBitRate")) {
4076 get_arg(arg
, sizeof(arg
), &p
);
4078 audio_enc
.bit_rate
= atoi(arg
) * 1000;
4079 } else if (!strcasecmp(cmd
, "AudioChannels")) {
4080 get_arg(arg
, sizeof(arg
), &p
);
4082 audio_enc
.channels
= atoi(arg
);
4083 } else if (!strcasecmp(cmd
, "AudioSampleRate")) {
4084 get_arg(arg
, sizeof(arg
), &p
);
4086 audio_enc
.sample_rate
= atoi(arg
);
4087 } else if (!strcasecmp(cmd
, "AudioQuality")) {
4088 get_arg(arg
, sizeof(arg
), &p
);
4090 // audio_enc.quality = atof(arg) * 1000;
4092 } else if (!strcasecmp(cmd
, "VideoBitRateRange")) {
4094 int minrate
, maxrate
;
4096 get_arg(arg
, sizeof(arg
), &p
);
4098 if (sscanf(arg
, "%d-%d", &minrate
, &maxrate
) == 2) {
4099 video_enc
.rc_min_rate
= minrate
* 1000;
4100 video_enc
.rc_max_rate
= maxrate
* 1000;
4102 fprintf(stderr
, "%s:%d: Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n",
4103 filename
, line_num
, arg
);
4107 } else if (!strcasecmp(cmd
, "Debug")) {
4109 get_arg(arg
, sizeof(arg
), &p
);
4110 video_enc
.debug
= strtol(arg
,0,0);
4112 } else if (!strcasecmp(cmd
, "Strict")) {
4114 get_arg(arg
, sizeof(arg
), &p
);
4115 video_enc
.strict_std_compliance
= atoi(arg
);
4117 } else if (!strcasecmp(cmd
, "VideoBufferSize")) {
4119 get_arg(arg
, sizeof(arg
), &p
);
4120 video_enc
.rc_buffer_size
= atoi(arg
) * 8*1024;
4122 } else if (!strcasecmp(cmd
, "VideoBitRateTolerance")) {
4124 get_arg(arg
, sizeof(arg
), &p
);
4125 video_enc
.bit_rate_tolerance
= atoi(arg
) * 1000;
4127 } else if (!strcasecmp(cmd
, "VideoBitRate")) {
4128 get_arg(arg
, sizeof(arg
), &p
);
4130 video_enc
.bit_rate
= atoi(arg
) * 1000;
4132 } else if (!strcasecmp(cmd
, "VideoSize")) {
4133 get_arg(arg
, sizeof(arg
), &p
);
4135 av_parse_video_frame_size(&video_enc
.width
, &video_enc
.height
, arg
);
4136 if ((video_enc
.width
% 16) != 0 ||
4137 (video_enc
.height
% 16) != 0) {
4138 fprintf(stderr
, "%s:%d: Image size must be a multiple of 16\n",
4139 filename
, line_num
);
4143 } else if (!strcasecmp(cmd
, "VideoFrameRate")) {
4144 get_arg(arg
, sizeof(arg
), &p
);
4146 AVRational frame_rate
;
4147 if (av_parse_video_frame_rate(&frame_rate
, arg
) < 0) {
4148 fprintf(stderr
, "Incorrect frame rate\n");
4151 video_enc
.time_base
.num
= frame_rate
.den
;
4152 video_enc
.time_base
.den
= frame_rate
.num
;
4155 } else if (!strcasecmp(cmd
, "VideoGopSize")) {
4156 get_arg(arg
, sizeof(arg
), &p
);
4158 video_enc
.gop_size
= atoi(arg
);
4159 } else if (!strcasecmp(cmd
, "VideoIntraOnly")) {
4161 video_enc
.gop_size
= 1;
4162 } else if (!strcasecmp(cmd
, "VideoHighQuality")) {
4164 video_enc
.mb_decision
= FF_MB_DECISION_BITS
;
4165 } else if (!strcasecmp(cmd
, "Video4MotionVector")) {
4167 video_enc
.mb_decision
= FF_MB_DECISION_BITS
; //FIXME remove
4168 video_enc
.flags
|= CODEC_FLAG_4MV
;
4170 } else if (!strcasecmp(cmd
, "AVOptionVideo") ||
4171 !strcasecmp(cmd
, "AVOptionAudio")) {
4173 AVCodecContext
*avctx
;
4175 get_arg(arg
, sizeof(arg
), &p
);
4176 get_arg(arg2
, sizeof(arg2
), &p
);
4177 if (!strcasecmp(cmd
, "AVOptionVideo")) {
4179 type
= AV_OPT_FLAG_VIDEO_PARAM
;
4182 type
= AV_OPT_FLAG_AUDIO_PARAM
;
4184 if (ffserver_opt_default(arg
, arg2
, avctx
, type
|AV_OPT_FLAG_ENCODING_PARAM
)) {
4185 fprintf(stderr
, "AVOption error: %s %s\n", arg
, arg2
);
4188 } else if (!strcasecmp(cmd
, "VideoTag")) {
4189 get_arg(arg
, sizeof(arg
), &p
);
4190 if ((strlen(arg
) == 4) && stream
)
4191 video_enc
.codec_tag
= ff_get_fourcc(arg
);
4192 } else if (!strcasecmp(cmd
, "BitExact")) {
4194 video_enc
.flags
|= CODEC_FLAG_BITEXACT
;
4195 } else if (!strcasecmp(cmd
, "DctFastint")) {
4197 video_enc
.dct_algo
= FF_DCT_FASTINT
;
4198 } else if (!strcasecmp(cmd
, "IdctSimple")) {
4200 video_enc
.idct_algo
= FF_IDCT_SIMPLE
;
4201 } else if (!strcasecmp(cmd
, "Qscale")) {
4202 get_arg(arg
, sizeof(arg
), &p
);
4204 video_enc
.flags
|= CODEC_FLAG_QSCALE
;
4205 video_enc
.global_quality
= FF_QP2LAMBDA
* atoi(arg
);
4207 } else if (!strcasecmp(cmd
, "VideoQDiff")) {
4208 get_arg(arg
, sizeof(arg
), &p
);
4210 video_enc
.max_qdiff
= atoi(arg
);
4211 if (video_enc
.max_qdiff
< 1 || video_enc
.max_qdiff
> 31) {
4212 fprintf(stderr
, "%s:%d: VideoQDiff out of range\n",
4213 filename
, line_num
);
4217 } else if (!strcasecmp(cmd
, "VideoQMax")) {
4218 get_arg(arg
, sizeof(arg
), &p
);
4220 video_enc
.qmax
= atoi(arg
);
4221 if (video_enc
.qmax
< 1 || video_enc
.qmax
> 31) {
4222 fprintf(stderr
, "%s:%d: VideoQMax out of range\n",
4223 filename
, line_num
);
4227 } else if (!strcasecmp(cmd
, "VideoQMin")) {
4228 get_arg(arg
, sizeof(arg
), &p
);
4230 video_enc
.qmin
= atoi(arg
);
4231 if (video_enc
.qmin
< 1 || video_enc
.qmin
> 31) {
4232 fprintf(stderr
, "%s:%d: VideoQMin out of range\n",
4233 filename
, line_num
);
4237 } else if (!strcasecmp(cmd
, "LumaElim")) {
4238 get_arg(arg
, sizeof(arg
), &p
);
4240 video_enc
.luma_elim_threshold
= atoi(arg
);
4241 } else if (!strcasecmp(cmd
, "ChromaElim")) {
4242 get_arg(arg
, sizeof(arg
), &p
);
4244 video_enc
.chroma_elim_threshold
= atoi(arg
);
4245 } else if (!strcasecmp(cmd
, "LumiMask")) {
4246 get_arg(arg
, sizeof(arg
), &p
);
4248 video_enc
.lumi_masking
= atof(arg
);
4249 } else if (!strcasecmp(cmd
, "DarkMask")) {
4250 get_arg(arg
, sizeof(arg
), &p
);
4252 video_enc
.dark_masking
= atof(arg
);
4253 } else if (!strcasecmp(cmd
, "NoVideo")) {
4254 video_id
= CODEC_ID_NONE
;
4255 } else if (!strcasecmp(cmd
, "NoAudio")) {
4256 audio_id
= CODEC_ID_NONE
;
4257 } else if (!strcasecmp(cmd
, "ACL")) {
4260 get_arg(arg
, sizeof(arg
), &p
);
4261 if (strcasecmp(arg
, "allow") == 0)
4262 acl
.action
= IP_ALLOW
;
4263 else if (strcasecmp(arg
, "deny") == 0)
4264 acl
.action
= IP_DENY
;
4266 fprintf(stderr
, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
4267 filename
, line_num
, arg
);
4271 get_arg(arg
, sizeof(arg
), &p
);
4273 if (resolve_host(&acl
.first
, arg
) != 0) {
4274 fprintf(stderr
, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4275 filename
, line_num
, arg
);
4278 acl
.last
= acl
.first
;
4280 get_arg(arg
, sizeof(arg
), &p
);
4283 if (resolve_host(&acl
.last
, arg
) != 0) {
4284 fprintf(stderr
, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4285 filename
, line_num
, arg
);
4291 IPAddressACL
*nacl
= av_mallocz(sizeof(*nacl
));
4292 IPAddressACL
**naclp
= 0;
4298 naclp
= &stream
->acl
;
4302 fprintf(stderr
, "%s:%d: ACL found not in <stream> or <feed>\n",
4303 filename
, line_num
);
4309 naclp
= &(*naclp
)->next
;
4314 } else if (!strcasecmp(cmd
, "RTSPOption")) {
4315 get_arg(arg
, sizeof(arg
), &p
);
4317 av_freep(&stream
->rtsp_option
);
4318 stream
->rtsp_option
= av_strdup(arg
);
4320 } else if (!strcasecmp(cmd
, "MulticastAddress")) {
4321 get_arg(arg
, sizeof(arg
), &p
);
4323 if (resolve_host(&stream
->multicast_ip
, arg
) != 0) {
4324 fprintf(stderr
, "%s:%d: Invalid host/IP address: %s\n",
4325 filename
, line_num
, arg
);
4328 stream
->is_multicast
= 1;
4329 stream
->loop
= 1; /* default is looping */
4331 } else if (!strcasecmp(cmd
, "MulticastPort")) {
4332 get_arg(arg
, sizeof(arg
), &p
);
4334 stream
->multicast_port
= atoi(arg
);
4335 } else if (!strcasecmp(cmd
, "MulticastTTL")) {
4336 get_arg(arg
, sizeof(arg
), &p
);
4338 stream
->multicast_ttl
= atoi(arg
);
4339 } else if (!strcasecmp(cmd
, "NoLoop")) {
4342 } else if (!strcasecmp(cmd
, "</Stream>")) {
4344 fprintf(stderr
, "%s:%d: No corresponding <Stream> for </Stream>\n",
4345 filename
, line_num
);
4348 if (stream
->feed
&& stream
->fmt
&& strcmp(stream
->fmt
->name
, "ffm") != 0) {
4349 if (audio_id
!= CODEC_ID_NONE
) {
4350 audio_enc
.codec_type
= CODEC_TYPE_AUDIO
;
4351 audio_enc
.codec_id
= audio_id
;
4352 add_codec(stream
, &audio_enc
);
4354 if (video_id
!= CODEC_ID_NONE
) {
4355 video_enc
.codec_type
= CODEC_TYPE_VIDEO
;
4356 video_enc
.codec_id
= video_id
;
4357 add_codec(stream
, &video_enc
);
4362 } else if (!strcasecmp(cmd
, "<Redirect")) {
4363 /*********************************************/
4365 if (stream
|| feed
|| redirect
) {
4366 fprintf(stderr
, "%s:%d: Already in a tag\n",
4367 filename
, line_num
);
4370 redirect
= av_mallocz(sizeof(FFStream
));
4371 *last_stream
= redirect
;
4372 last_stream
= &redirect
->next
;
4374 get_arg(redirect
->filename
, sizeof(redirect
->filename
), &p
);
4375 q
= strrchr(redirect
->filename
, '>');
4378 redirect
->stream_type
= STREAM_TYPE_REDIRECT
;
4380 } else if (!strcasecmp(cmd
, "URL")) {
4382 get_arg(redirect
->feed_filename
, sizeof(redirect
->feed_filename
), &p
);
4383 } else if (!strcasecmp(cmd
, "</Redirect>")) {
4385 fprintf(stderr
, "%s:%d: No corresponding <Redirect> for </Redirect>\n",
4386 filename
, line_num
);
4389 if (!redirect
->feed_filename
[0]) {
4390 fprintf(stderr
, "%s:%d: No URL found for <Redirect>\n",
4391 filename
, line_num
);
4396 } else if (!strcasecmp(cmd
, "LoadModule")) {
4397 get_arg(arg
, sizeof(arg
), &p
);
4401 fprintf(stderr
, "%s:%d: Module support not compiled into this version: '%s'\n",
4402 filename
, line_num
, arg
);
4406 fprintf(stderr
, "%s:%d: Incorrect keyword: '%s'\n",
4407 filename
, line_num
, cmd
);
4419 static void handle_child_exit(int sig
)
4424 while ((pid
= waitpid(-1, &status
, WNOHANG
)) > 0) {
4427 for (feed
= first_feed
; feed
; feed
= feed
->next
) {
4428 if (feed
->pid
== pid
) {
4429 int uptime
= time(0) - feed
->pid_start
;
4432 fprintf(stderr
, "%s: Pid %d exited with status %d after %d seconds\n", feed
->filename
, pid
, status
, uptime
);
4435 /* Turn off any more restarts */
4436 feed
->child_argv
= 0;
4441 need_to_start_children
= 1;
4444 static void opt_debug()
4447 ffserver_daemon
= 0;
4448 logfilename
[0] = '-';
4451 static void opt_show_help(void)
4453 printf("usage: ffserver [options]\n"
4454 "Hyper fast multi format Audio/Video streaming server\n");
4456 show_help_options(options
, "Main options:\n", 0, 0);
4459 static const OptionDef options
[] = {
4460 { "h", OPT_EXIT
, {(void*)opt_show_help
}, "show help" },
4461 { "version", OPT_EXIT
, {(void*)show_version
}, "show version" },
4462 { "L", OPT_EXIT
, {(void*)show_license
}, "show license" },
4463 { "formats", OPT_EXIT
, {(void*)show_formats
}, "show available formats, codecs, protocols, ..." },
4464 { "n", OPT_BOOL
, {(void *)&no_launch
}, "enable no-launch mode" },
4465 { "d", 0, {(void*)opt_debug
}, "enable debug mode" },
4466 { "f", HAS_ARG
| OPT_STRING
, {(void*)&config_filename
}, "use configfile instead of /etc/ffserver.conf", "configfile" },
4470 int main(int argc
, char **argv
)
4472 struct sigaction sigact
;
4478 config_filename
= "/etc/ffserver.conf";
4480 my_program_name
= argv
[0];
4481 my_program_dir
= getcwd(0, 0);
4482 ffserver_daemon
= 1;
4484 parse_options(argc
, argv
, options
, NULL
);
4486 unsetenv("http_proxy"); /* Kill the http_proxy */
4488 av_init_random(av_gettime() + (getpid() << 16), &random_state
);
4490 memset(&sigact
, 0, sizeof(sigact
));
4491 sigact
.sa_handler
= handle_child_exit
;
4492 sigact
.sa_flags
= SA_NOCLDSTOP
| SA_RESTART
;
4493 sigaction(SIGCHLD
, &sigact
, 0);
4495 if (parse_ffconfig(config_filename
) < 0) {
4496 fprintf(stderr
, "Incorrect config file - exiting.\n");
4500 /* open log file if needed */
4501 if (logfilename
[0] != '\0') {
4502 if (!strcmp(logfilename
, "-"))
4505 logfile
= fopen(logfilename
, "a");
4506 av_log_set_callback(http_av_log
);
4509 build_file_streams();
4511 build_feed_streams();
4513 compute_bandwidth();
4515 /* put the process in background and detach it from its TTY */
4516 if (ffserver_daemon
) {
4523 } else if (pid
> 0) {
4530 open("/dev/null", O_RDWR
);
4531 if (strcmp(logfilename
, "-") != 0) {
4541 signal(SIGPIPE
, SIG_IGN
);
4543 if (ffserver_daemon
)
4546 if (http_server() < 0) {
4547 http_log("Could not start server\n");