2 * Multiple format streaming server
3 * Copyright (c) 2000, 2001, 2002 Fabrice Bellard
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #define HAVE_AV_CONFIG_H
23 #include <netinet/in.h>
26 #include <sys/ioctl.h>
31 #include <sys/types.h>
32 #include <sys/socket.h>
34 #include <arpa/inet.h>
42 /* maximum number of simultaneous HTTP connections */
43 #define HTTP_MAX_CONNECTIONS 2000
46 HTTPSTATE_WAIT_REQUEST
,
47 HTTPSTATE_SEND_HEADER
,
48 HTTPSTATE_SEND_DATA_HEADER
,
49 HTTPSTATE_SEND_DATA
, /* sending TCP or UDP data */
50 HTTPSTATE_SEND_DATA_TRAILER
,
51 HTTPSTATE_RECEIVE_DATA
,
52 HTTPSTATE_WAIT_FEED
, /* wait for data from the feed */
53 HTTPSTATE_WAIT
, /* wait before sending next packets */
54 HTTPSTATE_WAIT_SHORT
, /* short wait for short term
55 bandwidth limitation */
58 RTSPSTATE_WAIT_REQUEST
,
62 const char *http_state
[] = {
79 #define IOBUFFER_INIT_SIZE 8192
81 /* coef for exponential mean for bitrate estimation in statistics */
84 /* timeouts are in ms */
85 #define HTTP_REQUEST_TIMEOUT (15 * 1000)
86 #define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
88 #define SYNC_TIMEOUT (10 * 1000)
95 /* context associated with one connection */
96 typedef struct HTTPContext
{
98 int fd
; /* socket file descriptor */
99 struct sockaddr_in from_addr
; /* origin */
100 struct pollfd
*poll_entry
; /* used when polling */
102 UINT8
*buffer_ptr
, *buffer_end
;
104 struct HTTPContext
*next
;
105 int got_key_frame
; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
109 /* input format handling */
110 AVFormatContext
*fmt_in
;
111 long start_time
; /* In milliseconds - this wraps fairly often */
112 INT64 first_pts
; /* initial pts value */
113 int pts_stream_index
; /* stream we choose as clock reference */
114 /* output format handling */
115 struct FFStream
*stream
;
116 /* -1 is invalid stream */
117 int feed_streams
[MAX_STREAMS
]; /* index of streams in the feed */
118 int switch_feed_streams
[MAX_STREAMS
]; /* index of streams in the feed */
120 AVFormatContext fmt_ctx
; /* instance of FFStream for one user */
121 int last_packet_sent
; /* true if last data packet was sent */
124 DataRateData datarate
;
131 int is_packetized
; /* if true, the stream is packetized */
132 int packet_stream_index
; /* current stream for output in state machine */
134 /* RTSP state specific */
135 UINT8
*pb_buffer
; /* XXX: use that in all the code */
137 int seq
; /* RTSP sequence number */
139 /* RTP state specific */
140 enum RTSPProtocol rtp_protocol
;
141 char session_id
[32]; /* session id */
142 AVFormatContext
*rtp_ctx
[MAX_STREAMS
];
143 URLContext
*rtp_handles
[MAX_STREAMS
];
144 /* RTP short term bandwidth limitation */
145 int packet_byte_count
;
146 int packet_start_time_us
; /* used for short durations (a few
150 /* each generated stream is described here */
154 STREAM_TYPE_REDIRECT
,
157 enum IPAddressAction
{
162 typedef struct IPAddressACL
{
163 struct IPAddressACL
*next
;
164 enum IPAddressAction action
;
165 struct in_addr first
;
169 /* description of each stream of the ffserver.conf file */
170 typedef struct FFStream
{
171 enum StreamType stream_type
;
172 char filename
[1024]; /* stream filename */
173 struct FFStream
*feed
; /* feed we are using (can be null if
178 int prebuffer
; /* Number of millseconds early to start */
179 long max_time
; /* Number of milliseconds to run */
181 AVStream
*streams
[MAX_STREAMS
];
182 int feed_streams
[MAX_STREAMS
]; /* index of streams in the feed */
183 char feed_filename
[1024]; /* file name of the feed storage, or
184 input file name for a stream */
189 pid_t pid
; /* Of ffmpeg process */
190 time_t pid_start
; /* Of ffmpeg process */
192 struct FFStream
*next
;
196 int feed_opened
; /* true if someone is writing to the feed */
197 int is_feed
; /* true if it is a feed */
200 INT64 feed_max_size
; /* maximum storage size */
201 INT64 feed_write_index
; /* current write position in feed (it wraps round) */
202 INT64 feed_size
; /* current size of feed */
203 struct FFStream
*next_feed
;
206 typedef struct FeedData
{
207 long long data_count
;
208 float avg_frame_size
; /* frame size averraged over last frames with exponential mean */
211 struct sockaddr_in my_http_addr
;
212 struct sockaddr_in my_rtsp_addr
;
214 char logfilename
[1024];
215 HTTPContext
*first_http_ctx
;
216 FFStream
*first_feed
; /* contains only feeds */
217 FFStream
*first_stream
; /* contains all streams, including feeds */
219 static void new_connection(int server_fd
, int is_rtsp
);
220 static void close_connection(HTTPContext
*c
);
223 static int handle_connection(HTTPContext
*c
);
224 static int http_parse_request(HTTPContext
*c
);
225 static int http_send_data(HTTPContext
*c
);
226 static void compute_stats(HTTPContext
*c
);
227 static int open_input_stream(HTTPContext
*c
, const char *info
);
228 static int http_start_receive_data(HTTPContext
*c
);
229 static int http_receive_data(HTTPContext
*c
);
230 static int compute_send_delay(HTTPContext
*c
);
233 static int rtsp_parse_request(HTTPContext
*c
);
234 static void rtsp_cmd_describe(HTTPContext
*c
, const char *url
);
235 static void rtsp_cmd_setup(HTTPContext
*c
, const char *url
, RTSPHeader
*h
);
236 static void rtsp_cmd_play(HTTPContext
*c
, const char *url
, RTSPHeader
*h
);
237 static void rtsp_cmd_pause(HTTPContext
*c
, const char *url
, RTSPHeader
*h
);
238 static void rtsp_cmd_teardown(HTTPContext
*c
, const char *url
, RTSPHeader
*h
);
241 static HTTPContext
*rtp_new_connection(HTTPContext
*rtsp_c
,
242 FFStream
*stream
, const char *session_id
);
243 static int rtp_new_av_stream(HTTPContext
*c
,
244 int stream_index
, struct sockaddr_in
*dest_addr
);
246 static const char *my_program_name
;
247 static const char *my_program_dir
;
249 static int ffserver_debug
;
250 static int ffserver_daemon
;
251 static int no_launch
;
252 static int need_to_start_children
;
254 int nb_max_connections
;
257 int nb_max_bandwidth
;
260 static long cur_time
; // Making this global saves on passing it around everywhere
262 static long gettime_ms(void)
266 gettimeofday(&tv
,NULL
);
267 return (long long)tv
.tv_sec
* 1000 + (tv
.tv_usec
/ 1000);
270 static FILE *logfile
= NULL
;
272 static void http_log(char *fmt
, ...)
278 vfprintf(logfile
, fmt
, ap
);
284 static void log_connection(HTTPContext
*c
)
286 char buf1
[32], buf2
[32], *p
;
292 /* XXX: reentrant function ? */
293 p
= inet_ntoa(c
->from_addr
.sin_addr
);
298 p
= buf2
+ strlen(p
) - 1;
301 http_log("%s - - [%s] \"%s %s %s\" %d %lld\n",
302 buf1
, buf2
, c
->method
, c
->url
, c
->protocol
, (c
->http_error ? c
->http_error
: 200), c
->data_count
);
305 static void update_datarate(DataRateData
*drd
, INT64 count
)
307 if (!drd
->time1
&& !drd
->count1
) {
308 drd
->time1
= drd
->time2
= cur_time
;
309 drd
->count1
= drd
->count2
= count
;
311 if (cur_time
- drd
->time2
> 5000) {
312 drd
->time1
= drd
->time2
;
313 drd
->count1
= drd
->count2
;
314 drd
->time2
= cur_time
;
320 /* In bytes per second */
321 static int compute_datarate(DataRateData
*drd
, INT64 count
)
323 if (cur_time
== drd
->time1
)
326 return ((count
- drd
->count1
) * 1000) / (cur_time
- drd
->time1
);
329 static int get_longterm_datarate(DataRateData
*drd
, INT64 count
)
331 /* You get the first 3 seconds flat out */
332 if (cur_time
- drd
->time1
< 3000)
335 return compute_datarate(drd
, count
);
339 static void start_children(FFStream
*feed
)
344 for (; feed
; feed
= feed
->next
) {
345 if (feed
->child_argv
&& !feed
->pid
) {
346 feed
->pid_start
= time(0);
351 fprintf(stderr
, "Unable to create children\n");
360 for (i
= 3; i
< 256; i
++) {
364 if (!ffserver_debug
) {
365 i
= open("/dev/null", O_RDWR
);
374 pstrcpy(pathname
, sizeof(pathname
), my_program_name
);
376 slash
= strrchr(pathname
, '/');
382 strcpy(slash
, "ffmpeg");
384 /* This is needed to make relative pathnames work */
385 chdir(my_program_dir
);
387 execvp(pathname
, feed
->child_argv
);
395 /* open a listening socket */
396 static int socket_open_listen(struct sockaddr_in
*my_addr
)
400 server_fd
= socket(AF_INET
,SOCK_STREAM
,0);
407 setsockopt(server_fd
, SOL_SOCKET
, SO_REUSEADDR
, &tmp
, sizeof(tmp
));
409 if (bind (server_fd
, (struct sockaddr
*) my_addr
, sizeof (*my_addr
)) < 0) {
415 if (listen (server_fd
, 5) < 0) {
420 fcntl(server_fd
, F_SETFL
, O_NONBLOCK
);
426 /* main loop of the http server */
427 static int http_server(void)
429 int server_fd
, ret
, rtsp_server_fd
, delay
, delay1
;
430 struct pollfd poll_table
[HTTP_MAX_CONNECTIONS
+ 2], *poll_entry
;
431 HTTPContext
*c
, *c_next
;
433 server_fd
= socket_open_listen(&my_http_addr
);
437 rtsp_server_fd
= socket_open_listen(&my_rtsp_addr
);
438 if (rtsp_server_fd
< 0)
441 http_log("ffserver started.\n");
443 start_children(first_feed
);
445 first_http_ctx
= NULL
;
447 first_http_ctx
= NULL
;
449 poll_entry
= poll_table
;
450 poll_entry
->fd
= server_fd
;
451 poll_entry
->events
= POLLIN
;
454 poll_entry
->fd
= rtsp_server_fd
;
455 poll_entry
->events
= POLLIN
;
458 /* wait for events on each HTTP handle */
465 case HTTPSTATE_SEND_HEADER
:
466 case RTSPSTATE_SEND_REPLY
:
467 c
->poll_entry
= poll_entry
;
469 poll_entry
->events
= POLLOUT
;
472 case HTTPSTATE_SEND_DATA_HEADER
:
473 case HTTPSTATE_SEND_DATA
:
474 case HTTPSTATE_SEND_DATA_TRAILER
:
475 if (!c
->is_packetized
) {
476 /* for TCP, we output as much as we can (may need to put a limit) */
477 c
->poll_entry
= poll_entry
;
479 poll_entry
->events
= POLLOUT
;
482 /* not strictly correct, but currently cannot add
483 more than one fd in poll entry */
487 case HTTPSTATE_WAIT_REQUEST
:
488 case HTTPSTATE_RECEIVE_DATA
:
489 case HTTPSTATE_WAIT_FEED
:
490 case RTSPSTATE_WAIT_REQUEST
:
491 /* need to catch errors */
492 c
->poll_entry
= poll_entry
;
494 poll_entry
->events
= POLLIN
;/* Maybe this will work */
498 c
->poll_entry
= NULL
;
499 delay1
= compute_send_delay(c
);
503 case HTTPSTATE_WAIT_SHORT
:
504 c
->poll_entry
= NULL
;
505 delay1
= 10; /* one tick wait XXX: 10 ms assumed */
510 c
->poll_entry
= NULL
;
516 /* wait for an event on one connection. We poll at least every
517 second to handle timeouts */
519 ret
= poll(poll_table
, poll_entry
- poll_table
, delay
);
522 cur_time
= gettime_ms();
524 if (need_to_start_children
) {
525 need_to_start_children
= 0;
526 start_children(first_feed
);
529 /* now handle the events */
530 for(c
= first_http_ctx
; c
!= NULL
; c
= c_next
) {
532 if (handle_connection(c
) < 0) {
533 /* close and free the connection */
539 poll_entry
= poll_table
;
540 /* new HTTP connection request ? */
541 if (poll_entry
->revents
& POLLIN
) {
542 new_connection(server_fd
, 0);
545 /* new RTSP connection request ? */
546 if (poll_entry
->revents
& POLLIN
) {
547 new_connection(rtsp_server_fd
, 1);
552 /* start waiting for a new HTTP/RTSP request */
553 static void start_wait_request(HTTPContext
*c
, int is_rtsp
)
555 c
->buffer_ptr
= c
->buffer
;
556 c
->buffer_end
= c
->buffer
+ c
->buffer_size
- 1; /* leave room for '\0' */
559 c
->timeout
= cur_time
+ RTSP_REQUEST_TIMEOUT
;
560 c
->state
= RTSPSTATE_WAIT_REQUEST
;
562 c
->timeout
= cur_time
+ HTTP_REQUEST_TIMEOUT
;
563 c
->state
= HTTPSTATE_WAIT_REQUEST
;
567 static void new_connection(int server_fd
, int is_rtsp
)
569 struct sockaddr_in from_addr
;
571 HTTPContext
*c
= NULL
;
573 len
= sizeof(from_addr
);
574 fd
= accept(server_fd
, (struct sockaddr
*)&from_addr
,
578 fcntl(fd
, F_SETFL
, O_NONBLOCK
);
580 /* XXX: should output a warning page when coming
581 close to the connection limit */
582 if (nb_connections
>= nb_max_connections
)
585 /* add a new connection */
586 c
= av_mallocz(sizeof(HTTPContext
));
590 c
->next
= first_http_ctx
;
593 c
->poll_entry
= NULL
;
594 c
->from_addr
= from_addr
;
595 c
->buffer_size
= IOBUFFER_INIT_SIZE
;
596 c
->buffer
= av_malloc(c
->buffer_size
);
601 start_wait_request(c
, is_rtsp
);
613 static void close_connection(HTTPContext
*c
)
615 HTTPContext
**cp
, *c1
;
617 AVFormatContext
*ctx
;
621 /* remove connection from list */
622 cp
= &first_http_ctx
;
623 while ((*cp
) != NULL
) {
632 /* remove connection associated resources */
636 /* close each frame parser */
637 for(i
=0;i
<c
->fmt_in
->nb_streams
;i
++) {
638 st
= c
->fmt_in
->streams
[i
];
639 if (st
->codec
.codec
) {
640 avcodec_close(&st
->codec
);
643 av_close_input_file(c
->fmt_in
);
646 /* free RTP output streams if any */
649 nb_streams
= c
->stream
->nb_streams
;
651 for(i
=0;i
<nb_streams
;i
++) {
654 av_write_trailer(ctx
);
657 h
= c
->rtp_handles
[i
];
663 nb_bandwidth
-= c
->bandwidth
;
664 av_freep(&c
->pb_buffer
);
670 static int handle_connection(HTTPContext
*c
)
675 case HTTPSTATE_WAIT_REQUEST
:
676 case RTSPSTATE_WAIT_REQUEST
:
678 if ((c
->timeout
- cur_time
) < 0)
680 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
))
683 /* no need to read if no events */
684 if (!(c
->poll_entry
->revents
& POLLIN
))
687 len
= read(c
->fd
, c
->buffer_ptr
, c
->buffer_end
- c
->buffer_ptr
);
689 if (errno
!= EAGAIN
&& errno
!= EINTR
)
691 } else if (len
== 0) {
694 /* search for end of request. XXX: not fully correct since garbage could come after the end */
696 c
->buffer_ptr
+= len
;
698 if ((ptr
>= c
->buffer
+ 2 && !memcmp(ptr
-2, "\n\n", 2)) ||
699 (ptr
>= c
->buffer
+ 4 && !memcmp(ptr
-4, "\r\n\r\n", 4))) {
700 /* request found : parse it and reply */
701 if (c
->state
== HTTPSTATE_WAIT_REQUEST
) {
702 ret
= http_parse_request(c
);
704 ret
= rtsp_parse_request(c
);
708 } else if (ptr
>= c
->buffer_end
) {
709 /* request too long: cannot do anything */
715 case HTTPSTATE_SEND_HEADER
:
716 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
))
719 /* no need to write if no events */
720 if (!(c
->poll_entry
->revents
& POLLOUT
))
722 len
= write(c
->fd
, c
->buffer_ptr
, c
->buffer_end
- c
->buffer_ptr
);
724 if (errno
!= EAGAIN
&& errno
!= EINTR
) {
725 /* error : close connection */
726 av_freep(&c
->pb_buffer
);
730 c
->buffer_ptr
+= len
;
732 c
->stream
->bytes_served
+= len
;
733 c
->data_count
+= len
;
734 if (c
->buffer_ptr
>= c
->buffer_end
) {
735 av_freep(&c
->pb_buffer
);
740 /* all the buffer was sent : synchronize to the incoming stream */
741 c
->state
= HTTPSTATE_SEND_DATA_HEADER
;
742 c
->buffer_ptr
= c
->buffer_end
= c
->buffer
;
747 case HTTPSTATE_SEND_DATA
:
748 case HTTPSTATE_SEND_DATA_HEADER
:
749 case HTTPSTATE_SEND_DATA_TRAILER
:
750 /* for packetized output, we consider we can always write (the
751 input streams sets the speed). It may be better to verify
752 that we do not rely too much on the kernel queues */
753 if (!c
->is_packetized
) {
754 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
))
757 /* no need to read if no events */
758 if (!(c
->poll_entry
->revents
& POLLOUT
))
761 if (http_send_data(c
) < 0)
764 case HTTPSTATE_RECEIVE_DATA
:
765 /* no need to read if no events */
766 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
))
768 if (!(c
->poll_entry
->revents
& POLLIN
))
770 if (http_receive_data(c
) < 0)
773 case HTTPSTATE_WAIT_FEED
:
774 /* no need to read if no events */
775 if (c
->poll_entry
->revents
& (POLLIN
| POLLERR
| POLLHUP
))
778 /* nothing to do, we'll be waken up by incoming feed packets */
782 /* if the delay expired, we can send new packets */
783 if (compute_send_delay(c
) <= 0)
784 c
->state
= HTTPSTATE_SEND_DATA
;
786 case HTTPSTATE_WAIT_SHORT
:
787 /* just return back to send data */
788 c
->state
= HTTPSTATE_SEND_DATA
;
791 case RTSPSTATE_SEND_REPLY
:
792 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
)) {
793 av_freep(&c
->pb_buffer
);
796 /* no need to write if no events */
797 if (!(c
->poll_entry
->revents
& POLLOUT
))
799 len
= write(c
->fd
, c
->buffer_ptr
, c
->buffer_end
- c
->buffer_ptr
);
801 if (errno
!= EAGAIN
&& errno
!= EINTR
) {
802 /* error : close connection */
803 av_freep(&c
->pb_buffer
);
807 c
->buffer_ptr
+= len
;
808 c
->data_count
+= len
;
809 if (c
->buffer_ptr
>= c
->buffer_end
) {
810 /* all the buffer was sent : wait for a new request */
811 av_freep(&c
->pb_buffer
);
812 start_wait_request(c
, 1);
816 case HTTPSTATE_READY
:
825 static int extract_rates(char *rates
, int ratelen
, const char *request
)
829 for (p
= request
; *p
&& *p
!= '\r' && *p
!= '\n'; ) {
830 if (strncasecmp(p
, "Pragma:", 7) == 0) {
831 const char *q
= p
+ 7;
833 while (*q
&& *q
!= '\n' && isspace(*q
))
836 if (strncasecmp(q
, "stream-switch-entry=", 20) == 0) {
842 memset(rates
, 0xff, ratelen
);
845 while (*q
&& *q
!= '\n' && *q
!= ':')
848 if (sscanf(q
, ":%d:%d", &stream_no
, &rate_no
) != 2) {
852 if (stream_no
< ratelen
&& stream_no
>= 0) {
853 rates
[stream_no
] = rate_no
;
856 while (*q
&& *q
!= '\n' && !isspace(*q
))
873 static int find_stream_in_feed(FFStream
*feed
, AVCodecContext
*codec
, int bit_rate
)
876 int best_bitrate
= 100000000;
879 for (i
= 0; i
< feed
->nb_streams
; i
++) {
880 AVCodecContext
*feed_codec
= &feed
->streams
[i
]->codec
;
882 if (feed_codec
->codec_id
!= codec
->codec_id
||
883 feed_codec
->sample_rate
!= codec
->sample_rate
||
884 feed_codec
->width
!= codec
->width
||
885 feed_codec
->height
!= codec
->height
) {
889 /* Potential stream */
891 /* We want the fastest stream less than bit_rate, or the slowest
892 * faster than bit_rate
895 if (feed_codec
->bit_rate
<= bit_rate
) {
896 if (best_bitrate
> bit_rate
|| feed_codec
->bit_rate
> best_bitrate
) {
897 best_bitrate
= feed_codec
->bit_rate
;
901 if (feed_codec
->bit_rate
< best_bitrate
) {
902 best_bitrate
= feed_codec
->bit_rate
;
911 static int modify_current_stream(HTTPContext
*c
, char *rates
)
914 FFStream
*req
= c
->stream
;
915 int action_required
= 0;
917 for (i
= 0; i
< req
->nb_streams
; i
++) {
918 AVCodecContext
*codec
= &req
->streams
[i
]->codec
;
922 c
->switch_feed_streams
[i
] = req
->feed_streams
[i
];
925 c
->switch_feed_streams
[i
] = find_stream_in_feed(req
->feed
, codec
, codec
->bit_rate
/ 2);
928 /* Wants off or slow */
929 c
->switch_feed_streams
[i
] = find_stream_in_feed(req
->feed
, codec
, codec
->bit_rate
/ 4);
931 /* This doesn't work well when it turns off the only stream! */
932 c
->switch_feed_streams
[i
] = -2;
933 c
->feed_streams
[i
] = -2;
938 if (c
->switch_feed_streams
[i
] >= 0 && c
->switch_feed_streams
[i
] != c
->feed_streams
[i
])
942 return action_required
;
946 static void do_switch_stream(HTTPContext
*c
, int i
)
948 if (c
->switch_feed_streams
[i
] >= 0) {
950 c
->feed_streams
[i
] = c
->switch_feed_streams
[i
];
953 /* Now update the stream */
955 c
->switch_feed_streams
[i
] = -1;
958 /* XXX: factorize in utils.c ? */
959 /* XXX: take care with different space meaning */
960 static void skip_spaces(const char **pp
)
964 while (*p
== ' ' || *p
== '\t')
969 static void get_word(char *buf
, int buf_size
, const char **pp
)
977 while (!isspace(*p
) && *p
!= '\0') {
978 if ((q
- buf
) < buf_size
- 1)
987 static int validate_acl(FFStream
*stream
, HTTPContext
*c
)
989 enum IPAddressAction last_action
= IP_DENY
;
991 struct in_addr
*src
= &c
->from_addr
.sin_addr
;
993 for (acl
= stream
->acl
; acl
; acl
= acl
->next
) {
994 if (src
->s_addr
>= acl
->first
.s_addr
&& src
->s_addr
<= acl
->last
.s_addr
) {
995 return (acl
->action
== IP_ALLOW
) ?
1 : 0;
997 last_action
= acl
->action
;
1000 /* Nothing matched, so return not the last action */
1001 return (last_action
== IP_DENY
) ?
1 : 0;
1004 /* parse http request and prepare header */
1005 static int http_parse_request(HTTPContext
*c
)
1010 int doing_asf_redirector
;
1012 int doing_rtsp_redirector
;
1014 char info
[1024], *filename
;
1018 const char *mime_type
;
1022 char *useragent
= 0;
1025 get_word(cmd
, sizeof(cmd
), (const char **)&p
);
1026 pstrcpy(c
->method
, sizeof(c
->method
), cmd
);
1028 if (!strcmp(cmd
, "GET"))
1030 else if (!strcmp(cmd
, "POST"))
1035 get_word(url
, sizeof(url
), (const char **)&p
);
1036 pstrcpy(c
->url
, sizeof(c
->url
), url
);
1038 get_word(protocol
, sizeof(protocol
), (const char **)&p
);
1039 if (strcmp(protocol
, "HTTP/1.0") && strcmp(protocol
, "HTTP/1.1"))
1042 pstrcpy(c
->protocol
, sizeof(c
->protocol
), protocol
);
1044 /* find the filename and the optional info string in the request */
1051 pstrcpy(info
, sizeof(info
), p
);
1057 for (p
= c
->buffer
; *p
&& *p
!= '\r' && *p
!= '\n'; ) {
1058 if (strncasecmp(p
, "User-Agent:", 11) == 0) {
1060 if (*useragent
&& *useragent
!= '\n' && isspace(*useragent
))
1064 p
= strchr(p
, '\n');
1071 if (strlen(filename
) > 4 && strcmp(".asx", filename
+ strlen(filename
) - 4) == 0) {
1073 filename
[strlen(filename
)-1] = 'f';
1078 if (strlen(filename
) > 4 && strcmp(".asf", filename
+ strlen(filename
) - 4) == 0 &&
1079 (!useragent
|| strncasecmp(useragent
, "NSPlayer", 8) != 0)) {
1080 /* if this isn't WMP or lookalike, return the redirector file */
1081 doing_asf_redirector
= 1;
1083 doing_asf_redirector
= 0;
1086 if (strlen(filename
) > 4 &&
1087 (strcmp(".rpm", filename
+ strlen(filename
) - 4) == 0 ||
1088 strcmp(".ram", filename
+ strlen(filename
) - 4) == 0)) {
1090 strcpy(filename
+ strlen(filename
)-2, "m");
1095 if (strlen(filename
) > 5 &&
1096 strcmp(".rtsp", filename
+ strlen(filename
) - 5) == 0) {
1101 doing_rtsp_redirector
= 1;
1102 /* compute filename by matching without the file extensions */
1103 pstrcpy(file1
, sizeof(file1
), filename
);
1104 p
= strrchr(file1
, '.');
1107 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
1108 pstrcpy(file2
, sizeof(file2
), stream
->filename
);
1109 p
= strrchr(file2
, '.');
1112 if (!strcmp(file1
, file2
)) {
1113 pstrcpy(url
, sizeof(url
), stream
->filename
);
1119 doing_rtsp_redirector
= 0;
1122 stream
= first_stream
;
1123 while (stream
!= NULL
) {
1124 if (!strcmp(stream
->filename
, filename
) && validate_acl(stream
, c
))
1126 stream
= stream
->next
;
1128 if (stream
== NULL
) {
1129 sprintf(msg
, "File '%s' not found", url
);
1134 memcpy(c
->feed_streams
, stream
->feed_streams
, sizeof(c
->feed_streams
));
1135 memset(c
->switch_feed_streams
, -1, sizeof(c
->switch_feed_streams
));
1137 if (stream
->stream_type
== STREAM_TYPE_REDIRECT
) {
1138 c
->http_error
= 301;
1140 q
+= sprintf(q
, "HTTP/1.0 301 Moved\r\n");
1141 q
+= sprintf(q
, "Location: %s\r\n", stream
->feed_filename
);
1142 q
+= sprintf(q
, "Content-type: text/html\r\n");
1143 q
+= sprintf(q
, "\r\n");
1144 q
+= sprintf(q
, "<html><head><title>Moved</title></head><body>\r\n");
1145 q
+= sprintf(q
, "You should be <a href=\"%s\">redirected</a>.\r\n", stream
->feed_filename
);
1146 q
+= sprintf(q
, "</body></html>\r\n");
1148 /* prepare output buffer */
1149 c
->buffer_ptr
= c
->buffer
;
1151 c
->state
= HTTPSTATE_SEND_HEADER
;
1155 /* If this is WMP, get the rate information */
1156 if (extract_rates(ratebuf
, sizeof(ratebuf
), c
->buffer
)) {
1157 if (modify_current_stream(c
, ratebuf
)) {
1158 for (i
= 0; i
< sizeof(c
->feed_streams
) / sizeof(c
->feed_streams
[0]); i
++) {
1159 if (c
->switch_feed_streams
[i
] >= 0)
1160 do_switch_stream(c
, i
);
1165 if (post
== 0 && stream
->stream_type
== STREAM_TYPE_LIVE
) {
1166 /* See if we meet the bandwidth requirements */
1167 for(i
=0;i
<stream
->nb_streams
;i
++) {
1168 AVStream
*st
= stream
->streams
[i
];
1169 switch(st
->codec
.codec_type
) {
1170 case CODEC_TYPE_AUDIO
:
1171 c
->bandwidth
+= st
->codec
.bit_rate
;
1173 case CODEC_TYPE_VIDEO
:
1174 c
->bandwidth
+= st
->codec
.bit_rate
;
1182 c
->bandwidth
/= 1000;
1183 nb_bandwidth
+= c
->bandwidth
;
1185 if (post
== 0 && nb_max_bandwidth
< nb_bandwidth
) {
1186 c
->http_error
= 200;
1188 q
+= sprintf(q
, "HTTP/1.0 200 Server too busy\r\n");
1189 q
+= sprintf(q
, "Content-type: text/html\r\n");
1190 q
+= sprintf(q
, "\r\n");
1191 q
+= sprintf(q
, "<html><head><title>Too busy</title></head><body>\r\n");
1192 q
+= sprintf(q
, "The server is too busy to serve your request at this time.<p>\r\n");
1193 q
+= sprintf(q
, "The bandwidth being served (including your stream) is %dkbit/sec, and this exceeds the limit of %dkbit/sec\r\n",
1194 nb_bandwidth
, nb_max_bandwidth
);
1195 q
+= sprintf(q
, "</body></html>\r\n");
1197 /* prepare output buffer */
1198 c
->buffer_ptr
= c
->buffer
;
1200 c
->state
= HTTPSTATE_SEND_HEADER
;
1204 if (doing_asx
|| doing_ram
|| doing_asf_redirector
||
1205 doing_rtsp_redirector
) {
1208 for (p
= c
->buffer
; *p
&& *p
!= '\r' && *p
!= '\n'; ) {
1209 if (strncasecmp(p
, "Host:", 5) == 0) {
1213 p
= strchr(p
, '\n');
1224 while (isspace(*hostinfo
))
1227 eoh
= strchr(hostinfo
, '\n');
1229 if (eoh
[-1] == '\r')
1232 if (eoh
- hostinfo
< sizeof(hostbuf
) - 1) {
1233 memcpy(hostbuf
, hostinfo
, eoh
- hostinfo
);
1234 hostbuf
[eoh
- hostinfo
] = 0;
1236 c
->http_error
= 200;
1239 q
+= sprintf(q
, "HTTP/1.0 200 ASX Follows\r\n");
1240 q
+= sprintf(q
, "Content-type: video/x-ms-asf\r\n");
1241 q
+= sprintf(q
, "\r\n");
1242 q
+= sprintf(q
, "<ASX Version=\"3\">\r\n");
1243 q
+= sprintf(q
, "<!-- Autogenerated by ffserver -->\r\n");
1244 q
+= sprintf(q
, "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n",
1245 hostbuf
, filename
, info
);
1246 q
+= sprintf(q
, "</ASX>\r\n");
1247 } else if (doing_ram
) {
1248 q
+= sprintf(q
, "HTTP/1.0 200 RAM Follows\r\n");
1249 q
+= sprintf(q
, "Content-type: audio/x-pn-realaudio\r\n");
1250 q
+= sprintf(q
, "\r\n");
1251 q
+= sprintf(q
, "# Autogenerated by ffserver\r\n");
1252 q
+= sprintf(q
, "http://%s/%s%s\r\n",
1253 hostbuf
, filename
, info
);
1254 } else if (doing_asf_redirector
) {
1255 q
+= sprintf(q
, "HTTP/1.0 200 ASF Redirect follows\r\n");
1256 q
+= sprintf(q
, "Content-type: video/x-ms-asf\r\n");
1257 q
+= sprintf(q
, "\r\n");
1258 q
+= sprintf(q
, "[Reference]\r\n");
1259 q
+= sprintf(q
, "Ref1=http://%s/%s%s\r\n",
1260 hostbuf
, filename
, info
);
1261 } else if (doing_rtsp_redirector
) {
1262 char hostname
[256], *p
;
1263 /* extract only hostname */
1264 pstrcpy(hostname
, sizeof(hostname
), hostbuf
);
1265 p
= strrchr(hostname
, ':');
1268 q
+= sprintf(q
, "HTTP/1.0 200 RTSP Redirect follows\r\n");
1269 /* XXX: incorrect mime type ? */
1270 q
+= sprintf(q
, "Content-type: application/x-rtsp\r\n");
1271 q
+= sprintf(q
, "\r\n");
1272 q
+= sprintf(q
, "rtsp://%s:%d/%s\r\n",
1273 hostname
, ntohs(my_rtsp_addr
.sin_port
),
1279 /* prepare output buffer */
1280 c
->buffer_ptr
= c
->buffer
;
1282 c
->state
= HTTPSTATE_SEND_HEADER
;
1288 sprintf(msg
, "ASX/RAM file not handled");
1292 stream
->conns_served
++;
1294 /* XXX: add there authenticate and IP match */
1297 /* if post, it means a feed is being sent */
1298 if (!stream
->is_feed
) {
1299 /* However it might be a status report from WMP! Lets log the data
1300 * as it might come in handy one day
1305 for (p
= c
->buffer
; *p
&& *p
!= '\r' && *p
!= '\n'; ) {
1306 if (strncasecmp(p
, "Pragma: log-line=", 17) == 0) {
1310 if (strncasecmp(p
, "Pragma: client-id=", 18) == 0) {
1311 client_id
= strtol(p
+ 18, 0, 10);
1313 p
= strchr(p
, '\n');
1321 char *eol
= strchr(logline
, '\n');
1326 if (eol
[-1] == '\r')
1328 http_log("%.*s\n", eol
- logline
, logline
);
1329 c
->suppress_log
= 1;
1334 http_log("\nGot request:\n%s\n", c
->buffer
);
1337 if (client_id
&& extract_rates(ratebuf
, sizeof(ratebuf
), c
->buffer
)) {
1340 /* Now we have to find the client_id */
1341 for (wmpc
= first_http_ctx
; wmpc
; wmpc
= wmpc
->next
) {
1342 if (wmpc
->wmp_client_id
== client_id
)
1347 if (modify_current_stream(wmpc
, ratebuf
)) {
1348 wmpc
->switch_pending
= 1;
1353 sprintf(msg
, "POST command not handled");
1356 if (http_start_receive_data(c
) < 0) {
1357 sprintf(msg
, "could not open feed");
1361 c
->state
= HTTPSTATE_RECEIVE_DATA
;
1366 if (strcmp(stream
->filename
+ strlen(stream
->filename
) - 4, ".asf") == 0) {
1367 http_log("\nGot request:\n%s\n", c
->buffer
);
1371 if (c
->stream
->stream_type
== STREAM_TYPE_STATUS
)
1374 /* open input stream */
1375 if (open_input_stream(c
, info
) < 0) {
1376 sprintf(msg
, "Input stream corresponding to '%s' not found", url
);
1380 /* prepare http header */
1382 q
+= sprintf(q
, "HTTP/1.0 200 OK\r\n");
1383 mime_type
= c
->stream
->fmt
->mime_type
;
1385 mime_type
= "application/x-octet_stream";
1386 q
+= sprintf(q
, "Pragma: no-cache\r\n");
1388 /* for asf, we need extra headers */
1389 if (!strcmp(c
->stream
->fmt
->name
,"asf_stream")) {
1390 /* Need to allocate a client id */
1392 c
->wmp_client_id
= random() & 0x7fffffff;
1394 q
+= sprintf(q
, "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
);
1395 mime_type
= "application/octet-stream";
1397 q
+= sprintf(q
, "Content-Type: %s\r\n", mime_type
);
1398 q
+= sprintf(q
, "\r\n");
1400 /* prepare output buffer */
1402 c
->buffer_ptr
= c
->buffer
;
1404 c
->state
= HTTPSTATE_SEND_HEADER
;
1407 c
->http_error
= 404;
1409 q
+= sprintf(q
, "HTTP/1.0 404 Not Found\r\n");
1410 q
+= sprintf(q
, "Content-type: %s\r\n", "text/html");
1411 q
+= sprintf(q
, "\r\n");
1412 q
+= sprintf(q
, "<HTML>\n");
1413 q
+= sprintf(q
, "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n");
1414 q
+= sprintf(q
, "<BODY>%s</BODY>\n", msg
);
1415 q
+= sprintf(q
, "</HTML>\n");
1417 /* prepare output buffer */
1418 c
->buffer_ptr
= c
->buffer
;
1420 c
->state
= HTTPSTATE_SEND_HEADER
;
1424 c
->http_error
= 200; /* horrible : we use this value to avoid
1425 going to the send data state */
1426 c
->state
= HTTPSTATE_SEND_HEADER
;
1430 static void fmt_bytecount(ByteIOContext
*pb
, INT64 count
)
1432 static const char *suffix
= " kMGTP";
1435 for (s
= suffix
; count
>= 100000 && s
[1]; count
/= 1000, s
++) {
1438 url_fprintf(pb
, "%lld%c", count
, *s
);
1441 static void compute_stats(HTTPContext
*c
)
1448 ByteIOContext pb1
, *pb
= &pb1
;
1450 if (url_open_dyn_buf(pb
) < 0) {
1451 /* XXX: return an error ? */
1452 c
->buffer_ptr
= c
->buffer
;
1453 c
->buffer_end
= c
->buffer
;
1457 url_fprintf(pb
, "HTTP/1.0 200 OK\r\n");
1458 url_fprintf(pb
, "Content-type: %s\r\n", "text/html");
1459 url_fprintf(pb
, "Pragma: no-cache\r\n");
1460 url_fprintf(pb
, "\r\n");
1462 url_fprintf(pb
, "<HEAD><TITLE>FFServer Status</TITLE>\n");
1463 if (c
->stream
->feed_filename
) {
1464 url_fprintf(pb
, "<link rel=\"shortcut icon\" href=\"%s\">\n", c
->stream
->feed_filename
);
1466 url_fprintf(pb
, "</HEAD>\n<BODY>");
1467 url_fprintf(pb
, "<H1>FFServer Status</H1>\n");
1469 url_fprintf(pb
, "<H2>Available Streams</H2>\n");
1470 url_fprintf(pb
, "<TABLE cellspacing=0 cellpadding=4>\n");
1471 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");
1472 stream
= first_stream
;
1473 while (stream
!= NULL
) {
1474 char sfilename
[1024];
1477 if (stream
->feed
!= stream
) {
1478 pstrcpy(sfilename
, sizeof(sfilename
) - 10, stream
->filename
);
1479 eosf
= sfilename
+ strlen(sfilename
);
1480 if (eosf
- sfilename
>= 4) {
1481 if (strcmp(eosf
- 4, ".asf") == 0) {
1482 strcpy(eosf
- 4, ".asx");
1483 } else if (strcmp(eosf
- 3, ".rm") == 0) {
1484 strcpy(eosf
- 3, ".ram");
1485 } else if (stream
->fmt
== &rtp_mux
) {
1486 /* generate a sample RTSP director - maybe should
1487 generate a .sdp file ? */
1488 eosf
= strrchr(sfilename
, '.');
1490 eosf
= sfilename
+ strlen(sfilename
);
1491 strcpy(eosf
, ".rtsp");
1495 url_fprintf(pb
, "<TR><TD><A HREF=\"/%s\">%s</A> ",
1496 sfilename
, stream
->filename
);
1497 url_fprintf(pb
, "<td align=right> %d <td align=right> ",
1498 stream
->conns_served
);
1499 fmt_bytecount(pb
, stream
->bytes_served
);
1500 switch(stream
->stream_type
) {
1501 case STREAM_TYPE_LIVE
:
1503 int audio_bit_rate
= 0;
1504 int video_bit_rate
= 0;
1505 const char *audio_codec_name
= "";
1506 const char *video_codec_name
= "";
1507 const char *audio_codec_name_extra
= "";
1508 const char *video_codec_name_extra
= "";
1510 for(i
=0;i
<stream
->nb_streams
;i
++) {
1511 AVStream
*st
= stream
->streams
[i
];
1512 AVCodec
*codec
= avcodec_find_encoder(st
->codec
.codec_id
);
1513 switch(st
->codec
.codec_type
) {
1514 case CODEC_TYPE_AUDIO
:
1515 audio_bit_rate
+= st
->codec
.bit_rate
;
1517 if (*audio_codec_name
)
1518 audio_codec_name_extra
= "...";
1519 audio_codec_name
= codec
->name
;
1522 case CODEC_TYPE_VIDEO
:
1523 video_bit_rate
+= st
->codec
.bit_rate
;
1525 if (*video_codec_name
)
1526 video_codec_name_extra
= "...";
1527 video_codec_name
= codec
->name
;
1534 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",
1536 (audio_bit_rate
+ video_bit_rate
) / 1000,
1537 video_bit_rate
/ 1000, video_codec_name
, video_codec_name_extra
,
1538 audio_bit_rate
/ 1000, audio_codec_name
, audio_codec_name_extra
);
1540 url_fprintf(pb
, "<TD>%s", stream
->feed
->filename
);
1542 url_fprintf(pb
, "<TD>%s", stream
->feed_filename
);
1544 url_fprintf(pb
, "\n");
1548 url_fprintf(pb
, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
1552 stream
= stream
->next
;
1554 url_fprintf(pb
, "</TABLE>\n");
1556 stream
= first_stream
;
1557 while (stream
!= NULL
) {
1558 if (stream
->feed
== stream
) {
1559 url_fprintf(pb
, "<h2>Feed %s</h2>", stream
->filename
);
1561 url_fprintf(pb
, "Running as pid %d.\n", stream
->pid
);
1563 #if defined(linux) && !defined(CONFIG_NOCUTILS)
1568 /* This is somewhat linux specific I guess */
1569 snprintf(ps_cmd
, sizeof(ps_cmd
),
1570 "ps -o \"%%cpu,cputime\" --no-headers %d",
1573 pid_stat
= popen(ps_cmd
, "r");
1578 if (fscanf(pid_stat
, "%10s %64s", cpuperc
,
1580 url_fprintf(pb
, "Currently using %s%% of the cpu. Total time used %s.\n",
1588 url_fprintf(pb
, "<p>");
1590 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");
1592 for (i
= 0; i
< stream
->nb_streams
; i
++) {
1593 AVStream
*st
= stream
->streams
[i
];
1594 AVCodec
*codec
= avcodec_find_encoder(st
->codec
.codec_id
);
1595 char *type
= "unknown";
1596 char parameters
[64];
1600 switch(st
->codec
.codec_type
) {
1601 case CODEC_TYPE_AUDIO
:
1604 case CODEC_TYPE_VIDEO
:
1606 sprintf(parameters
, "%dx%d, q=%d-%d, fps=%d", st
->codec
.width
, st
->codec
.height
,
1607 st
->codec
.qmin
, st
->codec
.qmax
, st
->codec
.frame_rate
/ FRAME_RATE_BASE
);
1612 url_fprintf(pb
, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
1613 i
, type
, st
->codec
.bit_rate
/1000, codec ? codec
->name
: "", parameters
);
1615 url_fprintf(pb
, "</table>\n");
1618 stream
= stream
->next
;
1624 AVCodecContext
*enc
;
1628 stream
= first_feed
;
1629 while (stream
!= NULL
) {
1630 url_fprintf(pb
, "<H1>Feed '%s'</H1>\n", stream
->filename
);
1631 url_fprintf(pb
, "<TABLE>\n");
1632 url_fprintf(pb
, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n");
1633 for(i
=0;i
<stream
->nb_streams
;i
++) {
1634 AVStream
*st
= stream
->streams
[i
];
1635 FeedData
*fdata
= st
->priv_data
;
1638 avcodec_string(buf
, sizeof(buf
), enc
);
1639 avg
= fdata
->avg_frame_size
* (float)enc
->rate
* 8.0;
1640 if (enc
->codec
->type
== CODEC_TYPE_AUDIO
&& enc
->frame_size
> 0)
1641 avg
/= enc
->frame_size
;
1642 url_fprintf(pb
, "<TR><TD>%s <TD> %d <TD> %Ld <TD> %0.1f\n",
1643 buf
, enc
->frame_number
, fdata
->data_count
, avg
/ 1000.0);
1645 url_fprintf(pb
, "</TABLE>\n");
1646 stream
= stream
->next_feed
;
1651 /* connection status */
1652 url_fprintf(pb
, "<H2>Connection Status</H2>\n");
1654 url_fprintf(pb
, "Number of connections: %d / %d<BR>\n",
1655 nb_connections
, nb_max_connections
);
1657 url_fprintf(pb
, "Bandwidth in use: %dk / %dk<BR>\n",
1658 nb_bandwidth
, nb_max_bandwidth
);
1660 url_fprintf(pb
, "<TABLE>\n");
1661 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");
1662 c1
= first_http_ctx
;
1664 while (c1
!= NULL
) {
1670 for (j
= 0; j
< c1
->stream
->nb_streams
; j
++) {
1671 if (!c1
->stream
->feed
) {
1672 bitrate
+= c1
->stream
->streams
[j
]->codec
.bit_rate
;
1674 if (c1
->feed_streams
[j
] >= 0) {
1675 bitrate
+= c1
->stream
->feed
->streams
[c1
->feed_streams
[j
]]->codec
.bit_rate
;
1682 p
= inet_ntoa(c1
->from_addr
.sin_addr
);
1683 url_fprintf(pb
, "<TR><TD><B>%d</B><TD>%s%s<TD>%s<TD>%s<TD>%s<td align=right>",
1685 c1
->stream ? c1
->stream
->filename
: "",
1686 c1
->state
== HTTPSTATE_RECEIVE_DATA ?
"(input)" : "",
1689 http_state
[c1
->state
]);
1690 fmt_bytecount(pb
, bitrate
);
1691 url_fprintf(pb
, "<td align=right>");
1692 fmt_bytecount(pb
, compute_datarate(&c1
->datarate
, c1
->data_count
) * 8);
1693 url_fprintf(pb
, "<td align=right>");
1694 fmt_bytecount(pb
, c1
->data_count
);
1695 url_fprintf(pb
, "\n");
1698 url_fprintf(pb
, "</TABLE>\n");
1703 url_fprintf(pb
, "<HR size=1 noshade>Generated at %s", p
);
1704 url_fprintf(pb
, "</BODY>\n</HTML>\n");
1706 len
= url_close_dyn_buf(pb
, &c
->pb_buffer
);
1707 c
->buffer_ptr
= c
->pb_buffer
;
1708 c
->buffer_end
= c
->pb_buffer
+ len
;
1711 /* check if the parser needs to be opened for stream i */
1712 static void open_parser(AVFormatContext
*s
, int i
)
1714 AVStream
*st
= s
->streams
[i
];
1717 if (!st
->codec
.codec
) {
1718 codec
= avcodec_find_decoder(st
->codec
.codec_id
);
1719 if (codec
&& (codec
->capabilities
& CODEC_CAP_PARSE_ONLY
)) {
1720 st
->codec
.parse_only
= 1;
1721 if (avcodec_open(&st
->codec
, codec
) < 0) {
1722 st
->codec
.parse_only
= 0;
1728 static int open_input_stream(HTTPContext
*c
, const char *info
)
1731 char input_filename
[1024];
1736 /* find file name */
1737 if (c
->stream
->feed
) {
1738 strcpy(input_filename
, c
->stream
->feed
->feed_filename
);
1739 buf_size
= FFM_PACKET_SIZE
;
1740 /* compute position (absolute time) */
1741 if (find_info_tag(buf
, sizeof(buf
), "date", info
)) {
1742 stream_pos
= parse_date(buf
, 0);
1743 } else if (find_info_tag(buf
, sizeof(buf
), "buffer", info
)) {
1744 int prebuffer
= strtol(buf
, 0, 10);
1745 stream_pos
= av_gettime() - prebuffer
* (INT64
)1000000;
1747 stream_pos
= av_gettime() - c
->stream
->prebuffer
* (INT64
)1000;
1750 strcpy(input_filename
, c
->stream
->feed_filename
);
1752 /* compute position (relative time) */
1753 if (find_info_tag(buf
, sizeof(buf
), "date", info
)) {
1754 stream_pos
= parse_date(buf
, 1);
1759 if (input_filename
[0] == '\0')
1763 { time_t when
= stream_pos
/ 1000000;
1764 http_log("Stream pos = %lld, time=%s", stream_pos
, ctime(&when
));
1769 if (av_open_input_file(&s
, input_filename
, NULL
, buf_size
, NULL
) < 0) {
1770 http_log("%s not found", input_filename
);
1775 /* open each parser */
1776 for(i
=0;i
<s
->nb_streams
;i
++)
1779 /* choose stream as clock source (we favorize video stream if
1780 present) for packet sending */
1781 c
->pts_stream_index
= 0;
1782 for(i
=0;i
<c
->stream
->nb_streams
;i
++) {
1783 if (c
->pts_stream_index
== 0 &&
1784 c
->stream
->streams
[i
]->codec
.codec_type
== CODEC_TYPE_VIDEO
) {
1785 c
->pts_stream_index
= i
;
1789 if (c
->fmt_in
->iformat
->read_seek
) {
1790 c
->fmt_in
->iformat
->read_seek(c
->fmt_in
, stream_pos
);
1792 /* set the start time (needed for maxtime and RTP packet timing) */
1793 c
->start_time
= cur_time
;
1794 c
->first_pts
= AV_NOPTS_VALUE
;
1798 /* currently desactivated because the new PTS handling is not
1800 //#define AV_READ_FRAME
1801 #ifdef AV_READ_FRAME
1803 /* XXX: generalize that in ffmpeg for picture/audio/data. Currently
1804 the return packet MUST NOT be freed */
1805 int av_read_frame(AVFormatContext
*s
, AVPacket
*pkt
)
1808 int len
, ret
, old_nb_streams
, i
;
1810 /* see if remaining frames must be parsed */
1812 if (s
->cur_len
> 0) {
1813 st
= s
->streams
[s
->cur_pkt
.stream_index
];
1814 len
= avcodec_parse_frame(&st
->codec
, &pkt
->data
, &pkt
->size
,
1815 s
->cur_ptr
, s
->cur_len
);
1817 /* error: get next packet */
1823 /* init pts counter if not done */
1824 if (st
->pts
.den
== 0) {
1825 switch(st
->codec
.codec_type
) {
1826 case CODEC_TYPE_AUDIO
:
1827 st
->pts_incr
= (INT64
)s
->pts_den
;
1828 av_frac_init(&st
->pts
, st
->pts
.val
, 0,
1829 (INT64
)s
->pts_num
* st
->codec
.sample_rate
);
1831 case CODEC_TYPE_VIDEO
:
1832 st
->pts_incr
= (INT64
)s
->pts_den
* FRAME_RATE_BASE
;
1833 av_frac_init(&st
->pts
, st
->pts
.val
, 0,
1834 (INT64
)s
->pts_num
* st
->codec
.frame_rate
);
1841 /* a frame was read: return it */
1842 pkt
->pts
= st
->pts
.val
;
1844 printf("add pts=%Lx num=%Lx den=%Lx incr=%Lx\n",
1845 st
->pts
.val
, st
->pts
.num
, st
->pts
.den
, st
->pts_incr
);
1847 switch(st
->codec
.codec_type
) {
1848 case CODEC_TYPE_AUDIO
:
1849 av_frac_add(&st
->pts
, st
->pts_incr
* st
->codec
.frame_size
);
1851 case CODEC_TYPE_VIDEO
:
1852 av_frac_add(&st
->pts
, st
->pts_incr
);
1857 pkt
->stream_index
= s
->cur_pkt
.stream_index
;
1858 /* we use the codec indication because it is
1859 more accurate than the demux flags */
1861 if (st
->codec
.key_frame
)
1862 pkt
->flags
|= PKT_FLAG_KEY
;
1867 /* free previous packet */
1868 av_free_packet(&s
->cur_pkt
);
1870 old_nb_streams
= s
->nb_streams
;
1871 ret
= av_read_packet(s
, &s
->cur_pkt
);
1874 /* open parsers for each new streams */
1875 for(i
= old_nb_streams
; i
< s
->nb_streams
; i
++)
1877 st
= s
->streams
[s
->cur_pkt
.stream_index
];
1879 /* update current pts (XXX: dts handling) from packet, or
1880 use current pts if none given */
1881 if (s
->cur_pkt
.pts
!= AV_NOPTS_VALUE
) {
1882 av_frac_set(&st
->pts
, s
->cur_pkt
.pts
);
1884 s
->cur_pkt
.pts
= st
->pts
.val
;
1886 if (!st
->codec
.codec
) {
1887 /* no codec opened: just return the raw packet */
1890 /* no codec opened: just update the pts by considering we
1891 have one frame and free the packet */
1892 if (st
->pts
.den
== 0) {
1893 switch(st
->codec
.codec_type
) {
1894 case CODEC_TYPE_AUDIO
:
1895 st
->pts_incr
= (INT64
)s
->pts_den
* st
->codec
.frame_size
;
1896 av_frac_init(&st
->pts
, st
->pts
.val
, 0,
1897 (INT64
)s
->pts_num
* st
->codec
.sample_rate
);
1899 case CODEC_TYPE_VIDEO
:
1900 st
->pts_incr
= (INT64
)s
->pts_den
* FRAME_RATE_BASE
;
1901 av_frac_init(&st
->pts
, st
->pts
.val
, 0,
1902 (INT64
)s
->pts_num
* st
->codec
.frame_rate
);
1908 av_frac_add(&st
->pts
, st
->pts_incr
);
1911 s
->cur_ptr
= s
->cur_pkt
.data
;
1912 s
->cur_len
= s
->cur_pkt
.size
;
1918 static int compute_send_delay(HTTPContext
*c
)
1920 INT64 cur_pts
, delta_pts
, next_pts
;
1923 /* compute current pts value from system time */
1924 cur_pts
= ((INT64
)(cur_time
- c
->start_time
) * c
->fmt_in
->pts_den
) /
1925 (c
->fmt_in
->pts_num
* 1000LL);
1926 /* compute the delta from the stream we choose as
1927 main clock (we do that to avoid using explicit
1928 buffers to do exact packet reordering for each
1930 /* XXX: really need to fix the number of streams */
1931 if (c
->pts_stream_index
>= c
->fmt_in
->nb_streams
)
1934 next_pts
= c
->fmt_in
->streams
[c
->pts_stream_index
]->pts
.val
;
1935 delta_pts
= next_pts
- cur_pts
;
1936 if (delta_pts
<= 0) {
1939 delay1
= (delta_pts
* 1000 * c
->fmt_in
->pts_num
) / c
->fmt_in
->pts_den
;
1945 /* just fall backs */
1946 int av_read_frame(AVFormatContext
*s
, AVPacket
*pkt
)
1948 return av_read_packet(s
, pkt
);
1951 static int compute_send_delay(HTTPContext
*c
)
1953 int datarate
= 8 * get_longterm_datarate(&c
->datarate
, c
->data_count
);
1955 if (datarate
> c
->bandwidth
* 2000) {
1963 static int http_prepare_data(HTTPContext
*c
)
1966 AVFormatContext
*ctx
;
1969 case HTTPSTATE_SEND_DATA_HEADER
:
1970 memset(&c
->fmt_ctx
, 0, sizeof(c
->fmt_ctx
));
1971 pstrcpy(c
->fmt_ctx
.author
, sizeof(c
->fmt_ctx
.author
),
1973 pstrcpy(c
->fmt_ctx
.comment
, sizeof(c
->fmt_ctx
.comment
),
1974 c
->stream
->comment
);
1975 pstrcpy(c
->fmt_ctx
.copyright
, sizeof(c
->fmt_ctx
.copyright
),
1976 c
->stream
->copyright
);
1977 pstrcpy(c
->fmt_ctx
.title
, sizeof(c
->fmt_ctx
.title
),
1980 /* open output stream by using specified codecs */
1981 c
->fmt_ctx
.oformat
= c
->stream
->fmt
;
1982 c
->fmt_ctx
.nb_streams
= c
->stream
->nb_streams
;
1983 for(i
=0;i
<c
->fmt_ctx
.nb_streams
;i
++) {
1985 st
= av_mallocz(sizeof(AVStream
));
1986 c
->fmt_ctx
.streams
[i
] = st
;
1987 /* if file or feed, then just take streams from FFStream struct */
1988 if (!c
->stream
->feed
||
1989 c
->stream
->feed
== c
->stream
)
1990 memcpy(st
, c
->stream
->streams
[i
], sizeof(AVStream
));
1992 memcpy(st
, c
->stream
->feed
->streams
[c
->stream
->feed_streams
[i
]],
1994 st
->codec
.frame_number
= 0; /* XXX: should be done in
1995 AVStream, not in codec */
1997 c
->got_key_frame
= 0;
1999 /* prepare header and save header data in a stream */
2000 if (url_open_dyn_buf(&c
->fmt_ctx
.pb
) < 0) {
2001 /* XXX: potential leak */
2004 c
->fmt_ctx
.pb
.is_streamed
= 1;
2006 av_write_header(&c
->fmt_ctx
);
2008 len
= url_close_dyn_buf(&c
->fmt_ctx
.pb
, &c
->pb_buffer
);
2009 c
->buffer_ptr
= c
->pb_buffer
;
2010 c
->buffer_end
= c
->pb_buffer
+ len
;
2012 c
->state
= HTTPSTATE_SEND_DATA
;
2013 c
->last_packet_sent
= 0;
2015 case HTTPSTATE_SEND_DATA
:
2016 /* find a new packet */
2020 /* read a packet from the input stream */
2021 if (c
->stream
->feed
) {
2022 ffm_set_write_index(c
->fmt_in
,
2023 c
->stream
->feed
->feed_write_index
,
2024 c
->stream
->feed
->feed_size
);
2027 if (c
->stream
->max_time
&&
2028 c
->stream
->max_time
+ c
->start_time
- cur_time
< 0) {
2029 /* We have timed out */
2030 c
->state
= HTTPSTATE_SEND_DATA_TRAILER
;
2032 if (1 || c
->is_packetized
) {
2033 if (compute_send_delay(c
) > 0) {
2034 c
->state
= HTTPSTATE_WAIT
;
2035 return 1; /* state changed */
2038 if (av_read_frame(c
->fmt_in
, &pkt
) < 0) {
2039 if (c
->stream
->feed
&& c
->stream
->feed
->feed_opened
) {
2040 /* if coming from feed, it means we reached the end of the
2041 ffm file, so must wait for more data */
2042 c
->state
= HTTPSTATE_WAIT_FEED
;
2043 return 1; /* state changed */
2045 /* must send trailer now because eof or error */
2046 c
->state
= HTTPSTATE_SEND_DATA_TRAILER
;
2049 /* update first pts if needed */
2050 if (c
->first_pts
== AV_NOPTS_VALUE
)
2051 c
->first_pts
= pkt
.pts
;
2053 /* send it to the appropriate stream */
2054 if (c
->stream
->feed
) {
2055 /* if coming from a feed, select the right stream */
2056 if (c
->switch_pending
) {
2057 c
->switch_pending
= 0;
2058 for(i
=0;i
<c
->stream
->nb_streams
;i
++) {
2059 if (c
->switch_feed_streams
[i
] == pkt
.stream_index
) {
2060 if (pkt
.flags
& PKT_FLAG_KEY
) {
2061 do_switch_stream(c
, i
);
2064 if (c
->switch_feed_streams
[i
] >= 0) {
2065 c
->switch_pending
= 1;
2069 for(i
=0;i
<c
->stream
->nb_streams
;i
++) {
2070 if (c
->feed_streams
[i
] == pkt
.stream_index
) {
2071 pkt
.stream_index
= i
;
2072 if (pkt
.flags
& PKT_FLAG_KEY
) {
2073 c
->got_key_frame
|= 1 << i
;
2075 /* See if we have all the key frames, then
2076 * we start to send. This logic is not quite
2077 * right, but it works for the case of a
2078 * single video stream with one or more
2079 * audio streams (for which every frame is
2080 * typically a key frame).
2082 if (!c
->stream
->send_on_key
||
2083 ((c
->got_key_frame
+ 1) >> c
->stream
->nb_streams
)) {
2089 AVCodecContext
*codec
;
2092 /* specific handling for RTP: we use several
2093 output stream (one for each RTP
2094 connection). XXX: need more abstract handling */
2095 if (c
->is_packetized
) {
2096 c
->packet_stream_index
= pkt
.stream_index
;
2097 ctx
= c
->rtp_ctx
[c
->packet_stream_index
];
2098 codec
= &ctx
->streams
[0]->codec
;
2102 codec
= &ctx
->streams
[pkt
.stream_index
]->codec
;
2105 codec
->key_frame
= ((pkt
.flags
& PKT_FLAG_KEY
) != 0);
2108 if (codec
->codec_type
== CODEC_TYPE_AUDIO
) {
2109 codec
->frame_size
= (codec
->sample_rate
* pkt
.duration
+ 500000) / 1000000;
2110 /* printf("Calculated size %d, from sr %d, duration %d\n", codec->frame_size, codec->sample_rate, pkt.duration); */
2114 if (c
->is_packetized
) {
2115 ret
= url_open_dyn_packet_buf(&ctx
->pb
,
2116 url_get_max_packet_size(c
->rtp_handles
[c
->packet_stream_index
]));
2117 c
->packet_byte_count
= 0;
2118 c
->packet_start_time_us
= av_gettime();
2120 ret
= url_open_dyn_buf(&ctx
->pb
);
2123 /* XXX: potential leak */
2126 if (av_write_frame(ctx
, pkt
.stream_index
, pkt
.data
, pkt
.size
)) {
2127 c
->state
= HTTPSTATE_SEND_DATA_TRAILER
;
2130 len
= url_close_dyn_buf(&ctx
->pb
, &c
->pb_buffer
);
2131 c
->buffer_ptr
= c
->pb_buffer
;
2132 c
->buffer_end
= c
->pb_buffer
+ len
;
2134 codec
->frame_number
++;
2136 #ifndef AV_READ_FRAME
2137 av_free_packet(&pkt
);
2144 case HTTPSTATE_SEND_DATA_TRAILER
:
2145 /* last packet test ? */
2146 if (c
->last_packet_sent
|| c
->is_packetized
)
2149 /* prepare header */
2150 if (url_open_dyn_buf(&ctx
->pb
) < 0) {
2151 /* XXX: potential leak */
2154 av_write_trailer(ctx
);
2155 len
= url_close_dyn_buf(&ctx
->pb
, &c
->pb_buffer
);
2156 c
->buffer_ptr
= c
->pb_buffer
;
2157 c
->buffer_end
= c
->pb_buffer
+ len
;
2159 c
->last_packet_sent
= 1;
2166 #define SHORT_TERM_BANDWIDTH 8000000
2168 /* should convert the format at the same time */
2169 static int http_send_data(HTTPContext
*c
)
2173 while (c
->buffer_ptr
>= c
->buffer_end
) {
2174 av_freep(&c
->pb_buffer
);
2175 ret
= http_prepare_data(c
);
2178 else if (ret
== 0) {
2181 /* state change requested */
2186 if (c
->buffer_ptr
< c
->buffer_end
) {
2187 if (c
->is_packetized
) {
2188 /* RTP/UDP data output */
2189 len
= c
->buffer_end
- c
->buffer_ptr
;
2191 /* fail safe - should never happen */
2193 c
->buffer_ptr
= c
->buffer_end
;
2196 len
= (c
->buffer_ptr
[0] << 24) |
2197 (c
->buffer_ptr
[1] << 16) |
2198 (c
->buffer_ptr
[2] << 8) |
2200 if (len
> (c
->buffer_end
- c
->buffer_ptr
))
2203 /* short term bandwidth limitation */
2204 dt
= av_gettime() - c
->packet_start_time_us
;
2208 if ((c
->packet_byte_count
+ len
) * (INT64
)1000000 >=
2209 (SHORT_TERM_BANDWIDTH
/ 8) * (INT64
)dt
) {
2210 /* bandwidth overflow : wait at most one tick and retry */
2211 c
->state
= HTTPSTATE_WAIT_SHORT
;
2216 url_write(c
->rtp_handles
[c
->packet_stream_index
],
2217 c
->buffer_ptr
, len
);
2218 c
->buffer_ptr
+= len
;
2219 c
->packet_byte_count
+= len
;
2221 /* TCP data output */
2222 len
= write(c
->fd
, c
->buffer_ptr
, c
->buffer_end
- c
->buffer_ptr
);
2224 if (errno
!= EAGAIN
&& errno
!= EINTR
) {
2225 /* error : close connection */
2231 c
->buffer_ptr
+= len
;
2234 c
->data_count
+= len
;
2235 update_datarate(&c
->datarate
, c
->data_count
);
2237 c
->stream
->bytes_served
+= len
;
2242 static int http_start_receive_data(HTTPContext
*c
)
2246 if (c
->stream
->feed_opened
)
2250 fd
= open(c
->stream
->feed_filename
, O_RDWR
);
2255 c
->stream
->feed_write_index
= ffm_read_write_index(fd
);
2256 c
->stream
->feed_size
= lseek(fd
, 0, SEEK_END
);
2257 lseek(fd
, 0, SEEK_SET
);
2259 /* init buffer input */
2260 c
->buffer_ptr
= c
->buffer
;
2261 c
->buffer_end
= c
->buffer
+ FFM_PACKET_SIZE
;
2262 c
->stream
->feed_opened
= 1;
2266 static int http_receive_data(HTTPContext
*c
)
2270 if (c
->buffer_end
> c
->buffer_ptr
) {
2273 len
= read(c
->fd
, c
->buffer_ptr
, c
->buffer_end
- c
->buffer_ptr
);
2275 if (errno
!= EAGAIN
&& errno
!= EINTR
) {
2276 /* error : close connection */
2279 } else if (len
== 0) {
2280 /* end of connection : close it */
2283 c
->buffer_ptr
+= len
;
2284 c
->data_count
+= len
;
2285 update_datarate(&c
->datarate
, c
->data_count
);
2289 if (c
->buffer_ptr
>= c
->buffer_end
) {
2290 FFStream
*feed
= c
->stream
;
2291 /* a packet has been received : write it in the store, except
2293 if (c
->data_count
> FFM_PACKET_SIZE
) {
2295 // printf("writing pos=0x%Lx size=0x%Lx\n", feed->feed_write_index, feed->feed_size);
2296 /* XXX: use llseek or url_seek */
2297 lseek(c
->feed_fd
, feed
->feed_write_index
, SEEK_SET
);
2298 write(c
->feed_fd
, c
->buffer
, FFM_PACKET_SIZE
);
2300 feed
->feed_write_index
+= FFM_PACKET_SIZE
;
2301 /* update file size */
2302 if (feed
->feed_write_index
> c
->stream
->feed_size
)
2303 feed
->feed_size
= feed
->feed_write_index
;
2305 /* handle wrap around if max file size reached */
2306 if (feed
->feed_write_index
>= c
->stream
->feed_max_size
)
2307 feed
->feed_write_index
= FFM_PACKET_SIZE
;
2310 ffm_write_write_index(c
->feed_fd
, feed
->feed_write_index
);
2312 /* wake up any waiting connections */
2313 for(c1
= first_http_ctx
; c1
!= NULL
; c1
= c1
->next
) {
2314 if (c1
->state
== HTTPSTATE_WAIT_FEED
&&
2315 c1
->stream
->feed
== c
->stream
->feed
) {
2316 c1
->state
= HTTPSTATE_SEND_DATA
;
2320 /* We have a header in our hands that contains useful data */
2322 AVInputFormat
*fmt_in
;
2323 ByteIOContext
*pb
= &s
.pb
;
2326 memset(&s
, 0, sizeof(s
));
2328 url_open_buf(pb
, c
->buffer
, c
->buffer_end
- c
->buffer
, URL_RDONLY
);
2329 pb
->buf_end
= c
->buffer_end
; /* ?? */
2330 pb
->is_streamed
= 1;
2332 /* use feed output format name to find corresponding input format */
2333 fmt_in
= av_find_input_format(feed
->fmt
->name
);
2337 s
.priv_data
= av_mallocz(fmt_in
->priv_data_size
);
2341 if (fmt_in
->read_header(&s
, 0) < 0) {
2342 av_freep(&s
.priv_data
);
2346 /* Now we have the actual streams */
2347 if (s
.nb_streams
!= feed
->nb_streams
) {
2348 av_freep(&s
.priv_data
);
2351 for (i
= 0; i
< s
.nb_streams
; i
++) {
2352 memcpy(&feed
->streams
[i
]->codec
,
2353 &s
.streams
[i
]->codec
, sizeof(AVCodecContext
));
2355 av_freep(&s
.priv_data
);
2357 c
->buffer_ptr
= c
->buffer
;
2362 c
->stream
->feed_opened
= 0;
2367 /********************************************************************/
2370 static void rtsp_reply_header(HTTPContext
*c
, enum RTSPStatusCode error_number
)
2377 switch(error_number
) {
2378 #define DEF(n, c, s) case c: str = s; break;
2379 #include "rtspcodes.h"
2382 str
= "Unknown Error";
2386 url_fprintf(c
->pb
, "RTSP/1.0 %d %s\r\n", error_number
, str
);
2387 url_fprintf(c
->pb
, "CSeq: %d\r\n", c
->seq
);
2389 /* output GMT time */
2393 p
= buf2
+ strlen(p
) - 1;
2396 url_fprintf(c
->pb
, "Date: %s GMT\r\n", buf2
);
2399 static void rtsp_reply_error(HTTPContext
*c
, enum RTSPStatusCode error_number
)
2401 rtsp_reply_header(c
, error_number
);
2402 url_fprintf(c
->pb
, "\r\n");
2405 static int rtsp_parse_request(HTTPContext
*c
)
2407 const char *p
, *p1
, *p2
;
2414 RTSPHeader header1
, *header
= &header1
;
2416 c
->buffer_ptr
[0] = '\0';
2419 get_word(cmd
, sizeof(cmd
), &p
);
2420 get_word(url
, sizeof(url
), &p
);
2421 get_word(protocol
, sizeof(protocol
), &p
);
2423 pstrcpy(c
->method
, sizeof(c
->method
), cmd
);
2424 pstrcpy(c
->url
, sizeof(c
->url
), url
);
2425 pstrcpy(c
->protocol
, sizeof(c
->protocol
), protocol
);
2428 if (url_open_dyn_buf(c
->pb
) < 0) {
2429 /* XXX: cannot do more */
2430 c
->pb
= NULL
; /* safety */
2434 /* check version name */
2435 if (strcmp(protocol
, "RTSP/1.0") != 0) {
2436 rtsp_reply_error(c
, RTSP_STATUS_VERSION
);
2440 /* parse each header line */
2441 memset(header
, 0, sizeof(RTSPHeader
));
2442 /* skip to next line */
2443 while (*p
!= '\n' && *p
!= '\0')
2447 while (*p
!= '\0') {
2448 p1
= strchr(p
, '\n');
2452 if (p2
> p
&& p2
[-1] == '\r')
2454 /* skip empty line */
2458 if (len
> sizeof(line
) - 1)
2459 len
= sizeof(line
) - 1;
2460 memcpy(line
, p
, len
);
2462 rtsp_parse_line(header
, line
);
2466 /* handle sequence number */
2467 c
->seq
= header
->seq
;
2469 if (!strcmp(cmd
, "DESCRIBE")) {
2470 rtsp_cmd_describe(c
, url
);
2471 } else if (!strcmp(cmd
, "SETUP")) {
2472 rtsp_cmd_setup(c
, url
, header
);
2473 } else if (!strcmp(cmd
, "PLAY")) {
2474 rtsp_cmd_play(c
, url
, header
);
2475 } else if (!strcmp(cmd
, "PAUSE")) {
2476 rtsp_cmd_pause(c
, url
, header
);
2477 } else if (!strcmp(cmd
, "TEARDOWN")) {
2478 rtsp_cmd_teardown(c
, url
, header
);
2480 rtsp_reply_error(c
, RTSP_STATUS_METHOD
);
2483 len
= url_close_dyn_buf(c
->pb
, &c
->pb_buffer
);
2484 c
->pb
= NULL
; /* safety */
2486 /* XXX: cannot do more */
2489 c
->buffer_ptr
= c
->pb_buffer
;
2490 c
->buffer_end
= c
->pb_buffer
+ len
;
2491 c
->state
= RTSPSTATE_SEND_REPLY
;
2495 static int prepare_sdp_description(HTTPContext
*c
,
2496 FFStream
*stream
, UINT8
**pbuffer
)
2498 ByteIOContext pb1
, *pb
= &pb1
;
2499 struct sockaddr_in my_addr
;
2500 int len
, i
, payload_type
;
2501 const char *ipstr
, *title
, *mediatype
;
2504 len
= sizeof(my_addr
);
2505 getsockname(c
->fd
, (struct sockaddr
*)&my_addr
, &len
);
2506 ipstr
= inet_ntoa(my_addr
.sin_addr
);
2508 if (url_open_dyn_buf(pb
) < 0)
2511 /* general media info */
2513 url_fprintf(pb
, "v=0\n");
2514 url_fprintf(pb
, "o=- 0 0 IN IP4 %s\n", ipstr
);
2515 title
= stream
->title
;
2516 if (title
[0] == '\0')
2518 url_fprintf(pb
, "s=%s\n", title
);
2519 if (stream
->comment
[0] != '\0')
2520 url_fprintf(pb
, "i=%s\n", stream
->comment
);
2522 /* for each stream, we output the necessary info */
2523 for(i
= 0; i
< stream
->nb_streams
; i
++) {
2524 st
= stream
->streams
[i
];
2525 switch(st
->codec
.codec_type
) {
2526 case CODEC_TYPE_AUDIO
:
2527 mediatype
= "audio";
2529 case CODEC_TYPE_VIDEO
:
2530 mediatype
= "video";
2533 mediatype
= "application";
2536 /* XXX: the port indication is not correct (but should be correct
2538 payload_type
= rtp_get_payload_type(&st
->codec
);
2540 url_fprintf(pb
, "m=%s %d RTP/AVP %d\n",
2541 mediatype
, 0, payload_type
);
2542 url_fprintf(pb
, "a=control:streamid=%d\n", i
);
2544 return url_close_dyn_buf(pb
, pbuffer
);
2547 static void rtsp_cmd_describe(HTTPContext
*c
, const char *url
)
2555 /* find which url is asked */
2556 url_split(NULL
, 0, NULL
, 0, NULL
, path1
, sizeof(path1
), url
);
2561 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
2562 if (!stream
->is_feed
&& stream
->fmt
== &rtp_mux
&&
2563 !strcmp(path
, stream
->filename
)) {
2567 /* no stream found */
2568 rtsp_reply_error(c
, RTSP_STATUS_SERVICE
); /* XXX: right error ? */
2572 /* prepare the media description in sdp format */
2573 content_length
= prepare_sdp_description(c
, stream
, &content
);
2574 if (content_length
< 0) {
2575 rtsp_reply_error(c
, RTSP_STATUS_INTERNAL
);
2578 rtsp_reply_header(c
, RTSP_STATUS_OK
);
2579 url_fprintf(c
->pb
, "Content-Type: application/sdp\r\n");
2580 url_fprintf(c
->pb
, "Content-Length: %d\r\n", content_length
);
2581 url_fprintf(c
->pb
, "\r\n");
2582 put_buffer(c
->pb
, content
, content_length
);
2585 static HTTPContext
*find_rtp_session(const char *session_id
)
2589 if (session_id
[0] == '\0')
2592 for(c
= first_http_ctx
; c
!= NULL
; c
= c
->next
) {
2593 if (!strcmp(c
->session_id
, session_id
))
2599 RTSPTransportField
*find_transport(RTSPHeader
*h
, enum RTSPProtocol protocol
)
2601 RTSPTransportField
*th
;
2604 for(i
=0;i
<h
->nb_transports
;i
++) {
2605 th
= &h
->transports
[i
];
2606 if (th
->protocol
== protocol
)
2612 static void rtsp_cmd_setup(HTTPContext
*c
, const char *url
,
2616 int stream_index
, port
;
2621 RTSPTransportField
*th
;
2622 struct sockaddr_in dest_addr
;
2623 RTSPActionServerSetup setup
;
2625 /* find which url is asked */
2626 url_split(NULL
, 0, NULL
, 0, NULL
, path1
, sizeof(path1
), url
);
2631 /* now check each stream */
2632 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
2633 if (!stream
->is_feed
&& stream
->fmt
== &rtp_mux
) {
2634 /* accept aggregate filenames only if single stream */
2635 if (!strcmp(path
, stream
->filename
)) {
2636 if (stream
->nb_streams
!= 1) {
2637 rtsp_reply_error(c
, RTSP_STATUS_AGGREGATE
);
2644 for(stream_index
= 0; stream_index
< stream
->nb_streams
;
2646 snprintf(buf
, sizeof(buf
), "%s/streamid=%d",
2647 stream
->filename
, stream_index
);
2648 if (!strcmp(path
, buf
))
2653 /* no stream found */
2654 rtsp_reply_error(c
, RTSP_STATUS_SERVICE
); /* XXX: right error ? */
2658 /* generate session id if needed */
2659 if (h
->session_id
[0] == '\0') {
2660 snprintf(h
->session_id
, sizeof(h
->session_id
),
2661 "%08x%08x", (int)random(), (int)random());
2664 /* find rtp session, and create it if none found */
2665 rtp_c
= find_rtp_session(h
->session_id
);
2667 rtp_c
= rtp_new_connection(c
, stream
, h
->session_id
);
2669 rtsp_reply_error(c
, RTSP_STATUS_BANDWIDTH
);
2673 /* open input stream */
2674 if (open_input_stream(rtp_c
, "") < 0) {
2675 rtsp_reply_error(c
, RTSP_STATUS_INTERNAL
);
2679 /* always prefer UDP */
2680 th
= find_transport(h
, RTSP_PROTOCOL_RTP_UDP
);
2682 th
= find_transport(h
, RTSP_PROTOCOL_RTP_TCP
);
2684 rtsp_reply_error(c
, RTSP_STATUS_TRANSPORT
);
2688 rtp_c
->rtp_protocol
= th
->protocol
;
2691 /* test if stream is OK (test needed because several SETUP needs
2692 to be done for a given file) */
2693 if (rtp_c
->stream
!= stream
) {
2694 rtsp_reply_error(c
, RTSP_STATUS_SERVICE
);
2698 /* test if stream is already set up */
2699 if (rtp_c
->rtp_ctx
[stream_index
]) {
2700 rtsp_reply_error(c
, RTSP_STATUS_STATE
);
2704 /* check transport */
2705 th
= find_transport(h
, rtp_c
->rtp_protocol
);
2706 if (!th
|| (th
->protocol
== RTSP_PROTOCOL_RTP_UDP
&&
2707 th
->client_port_min
<= 0)) {
2708 rtsp_reply_error(c
, RTSP_STATUS_TRANSPORT
);
2712 /* setup default options */
2713 setup
.transport_option
[0] = '\0';
2714 dest_addr
= rtp_c
->from_addr
;
2715 dest_addr
.sin_port
= htons(th
->client_port_min
);
2717 /* add transport option if needed */
2718 if (ff_rtsp_callback
) {
2719 setup
.ipaddr
= ntohl(dest_addr
.sin_addr
.s_addr
);
2720 if (ff_rtsp_callback(RTSP_ACTION_SERVER_SETUP
, rtp_c
->session_id
,
2721 (char *)&setup
, sizeof(setup
),
2722 stream
->rtsp_option
) < 0) {
2723 rtsp_reply_error(c
, RTSP_STATUS_TRANSPORT
);
2726 dest_addr
.sin_addr
.s_addr
= htonl(setup
.ipaddr
);
2730 if (rtp_new_av_stream(rtp_c
, stream_index
, &dest_addr
) < 0) {
2731 rtsp_reply_error(c
, RTSP_STATUS_TRANSPORT
);
2735 /* now everything is OK, so we can send the connection parameters */
2736 rtsp_reply_header(c
, RTSP_STATUS_OK
);
2738 url_fprintf(c
->pb
, "Session: %s\r\n", rtp_c
->session_id
);
2740 switch(rtp_c
->rtp_protocol
) {
2741 case RTSP_PROTOCOL_RTP_UDP
:
2742 port
= rtp_get_local_port(rtp_c
->rtp_handles
[stream_index
]);
2743 url_fprintf(c
->pb
, "Transport: RTP/AVP/UDP;unicast;"
2744 "client_port=%d-%d;server_port=%d-%d",
2745 th
->client_port_min
, th
->client_port_min
+ 1,
2748 case RTSP_PROTOCOL_RTP_TCP
:
2749 url_fprintf(c
->pb
, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
2750 stream_index
* 2, stream_index
* 2 + 1);
2755 if (setup
.transport_option
[0] != '\0') {
2756 url_fprintf(c
->pb
, ";%s", setup
.transport_option
);
2758 url_fprintf(c
->pb
, "\r\n");
2761 url_fprintf(c
->pb
, "\r\n");
2765 /* find an rtp connection by using the session ID. Check consistency
2767 static HTTPContext
*find_rtp_session_with_url(const char *url
,
2768 const char *session_id
)
2774 rtp_c
= find_rtp_session(session_id
);
2778 /* find which url is asked */
2779 url_split(NULL
, 0, NULL
, 0, NULL
, path1
, sizeof(path1
), url
);
2783 if (strcmp(path
, rtp_c
->stream
->filename
) != 0)
2788 static void rtsp_cmd_play(HTTPContext
*c
, const char *url
, RTSPHeader
*h
)
2792 rtp_c
= find_rtp_session_with_url(url
, h
->session_id
);
2794 rtsp_reply_error(c
, RTSP_STATUS_SESSION
);
2798 if (rtp_c
->state
!= HTTPSTATE_SEND_DATA
&&
2799 rtp_c
->state
!= HTTPSTATE_WAIT_FEED
&&
2800 rtp_c
->state
!= HTTPSTATE_READY
) {
2801 rtsp_reply_error(c
, RTSP_STATUS_STATE
);
2805 rtp_c
->state
= HTTPSTATE_SEND_DATA
;
2807 /* now everything is OK, so we can send the connection parameters */
2808 rtsp_reply_header(c
, RTSP_STATUS_OK
);
2810 url_fprintf(c
->pb
, "Session: %s\r\n", rtp_c
->session_id
);
2811 url_fprintf(c
->pb
, "\r\n");
2814 static void rtsp_cmd_pause(HTTPContext
*c
, const char *url
, RTSPHeader
*h
)
2818 rtp_c
= find_rtp_session_with_url(url
, h
->session_id
);
2820 rtsp_reply_error(c
, RTSP_STATUS_SESSION
);
2824 if (rtp_c
->state
!= HTTPSTATE_SEND_DATA
&&
2825 rtp_c
->state
!= HTTPSTATE_WAIT_FEED
) {
2826 rtsp_reply_error(c
, RTSP_STATUS_STATE
);
2830 rtp_c
->state
= HTTPSTATE_READY
;
2832 /* now everything is OK, so we can send the connection parameters */
2833 rtsp_reply_header(c
, RTSP_STATUS_OK
);
2835 url_fprintf(c
->pb
, "Session: %s\r\n", rtp_c
->session_id
);
2836 url_fprintf(c
->pb
, "\r\n");
2839 static void rtsp_cmd_teardown(HTTPContext
*c
, const char *url
, RTSPHeader
*h
)
2843 rtp_c
= find_rtp_session_with_url(url
, h
->session_id
);
2845 rtsp_reply_error(c
, RTSP_STATUS_SESSION
);
2849 /* abort the session */
2850 close_connection(rtp_c
);
2852 if (ff_rtsp_callback
) {
2853 ff_rtsp_callback(RTSP_ACTION_SERVER_TEARDOWN
, rtp_c
->session_id
,
2855 rtp_c
->stream
->rtsp_option
);
2858 /* now everything is OK, so we can send the connection parameters */
2859 rtsp_reply_header(c
, RTSP_STATUS_OK
);
2861 url_fprintf(c
->pb
, "Session: %s\r\n", rtp_c
->session_id
);
2862 url_fprintf(c
->pb
, "\r\n");
2866 /********************************************************************/
2869 static HTTPContext
*rtp_new_connection(HTTPContext
*rtsp_c
,
2870 FFStream
*stream
, const char *session_id
)
2872 HTTPContext
*c
= NULL
;
2874 /* XXX: should output a warning page when coming
2875 close to the connection limit */
2876 if (nb_connections
>= nb_max_connections
)
2879 /* add a new connection */
2880 c
= av_mallocz(sizeof(HTTPContext
));
2885 c
->poll_entry
= NULL
;
2886 c
->from_addr
= rtsp_c
->from_addr
;
2887 c
->buffer_size
= IOBUFFER_INIT_SIZE
;
2888 c
->buffer
= av_malloc(c
->buffer_size
);
2893 pstrcpy(c
->session_id
, sizeof(c
->session_id
), session_id
);
2894 c
->state
= HTTPSTATE_READY
;
2895 c
->is_packetized
= 1;
2896 /* protocol is shown in statistics */
2897 pstrcpy(c
->protocol
, sizeof(c
->protocol
), "RTP");
2899 c
->next
= first_http_ctx
;
2911 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
2912 command). if dest_addr is NULL, then TCP tunneling in RTSP is
2914 static int rtp_new_av_stream(HTTPContext
*c
,
2915 int stream_index
, struct sockaddr_in
*dest_addr
)
2917 AVFormatContext
*ctx
;
2923 /* now we can open the relevant output stream */
2924 ctx
= av_mallocz(sizeof(AVFormatContext
));
2927 ctx
->oformat
= &rtp_mux
;
2929 st
= av_mallocz(sizeof(AVStream
));
2932 ctx
->nb_streams
= 1;
2933 ctx
->streams
[0] = st
;
2935 if (!c
->stream
->feed
||
2936 c
->stream
->feed
== c
->stream
) {
2937 memcpy(st
, c
->stream
->streams
[stream_index
], sizeof(AVStream
));
2940 c
->stream
->feed
->streams
[c
->stream
->feed_streams
[stream_index
]],
2945 /* build destination RTP address */
2946 ipaddr
= inet_ntoa(dest_addr
->sin_addr
);
2948 snprintf(ctx
->filename
, sizeof(ctx
->filename
),
2949 "rtp://%s:%d", ipaddr
, ntohs(dest_addr
->sin_port
));
2951 printf("open %s\n", ctx
->filename
);
2953 if (url_open(&h
, ctx
->filename
, URL_WRONLY
) < 0)
2955 c
->rtp_handles
[stream_index
] = h
;
2960 /* normally, no packets should be output here, but the packet size may be checked */
2961 if (url_open_dyn_packet_buf(&ctx
->pb
,
2962 url_get_max_packet_size(h
)) < 0) {
2963 /* XXX: close stream */
2966 if (av_write_header(ctx
) < 0) {
2973 url_close_dyn_buf(&ctx
->pb
, &dummy_buf
);
2976 c
->rtp_ctx
[stream_index
] = ctx
;
2980 /********************************************************************/
2981 /* ffserver initialization */
2983 AVStream
*add_av_stream1(FFStream
*stream
, AVCodecContext
*codec
)
2987 fst
= av_mallocz(sizeof(AVStream
));
2990 fst
->priv_data
= av_mallocz(sizeof(FeedData
));
2991 memcpy(&fst
->codec
, codec
, sizeof(AVCodecContext
));
2992 stream
->streams
[stream
->nb_streams
++] = fst
;
2996 /* return the stream number in the feed */
2997 int add_av_stream(FFStream
*feed
,
3001 AVCodecContext
*av
, *av1
;
3005 for(i
=0;i
<feed
->nb_streams
;i
++) {
3006 st
= feed
->streams
[i
];
3008 if (av1
->codec_id
== av
->codec_id
&&
3009 av1
->codec_type
== av
->codec_type
&&
3010 av1
->bit_rate
== av
->bit_rate
) {
3012 switch(av
->codec_type
) {
3013 case CODEC_TYPE_AUDIO
:
3014 if (av1
->channels
== av
->channels
&&
3015 av1
->sample_rate
== av
->sample_rate
)
3018 case CODEC_TYPE_VIDEO
:
3019 if (av1
->width
== av
->width
&&
3020 av1
->height
== av
->height
&&
3021 av1
->frame_rate
== av
->frame_rate
&&
3022 av1
->gop_size
== av
->gop_size
)
3031 fst
= add_av_stream1(feed
, av
);
3034 return feed
->nb_streams
- 1;
3039 void remove_stream(FFStream
*stream
)
3043 while (*ps
!= NULL
) {
3044 if (*ps
== stream
) {
3052 /* compute the needed AVStream for each file */
3053 void build_file_streams(void)
3055 FFStream
*stream
, *stream_next
;
3056 AVFormatContext
*infile
;
3059 /* gather all streams */
3060 for(stream
= first_stream
; stream
!= NULL
; stream
= stream_next
) {
3061 stream_next
= stream
->next
;
3062 if (stream
->stream_type
== STREAM_TYPE_LIVE
&&
3064 /* the stream comes from a file */
3065 /* try to open the file */
3067 if (av_open_input_file(&infile
, stream
->feed_filename
,
3068 NULL
, 0, NULL
) < 0) {
3069 http_log("%s not found", stream
->feed_filename
);
3070 /* remove stream (no need to spend more time on it) */
3072 remove_stream(stream
);
3074 /* find all the AVStreams inside and reference them in
3076 if (av_find_stream_info(infile
) < 0) {
3077 http_log("Could not find codec parameters from '%s'",
3078 stream
->feed_filename
);
3079 av_close_input_file(infile
);
3082 for(i
=0;i
<infile
->nb_streams
;i
++) {
3083 add_av_stream1(stream
, &infile
->streams
[i
]->codec
);
3085 av_close_input_file(infile
);
3091 /* compute the needed AVStream for each feed */
3092 void build_feed_streams(void)
3094 FFStream
*stream
, *feed
;
3097 /* gather all streams */
3098 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
3099 feed
= stream
->feed
;
3101 if (!stream
->is_feed
) {
3102 /* we handle a stream coming from a feed */
3103 for(i
=0;i
<stream
->nb_streams
;i
++) {
3104 stream
->feed_streams
[i
] = add_av_stream(feed
, stream
->streams
[i
]);
3110 /* gather all streams */
3111 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
3112 feed
= stream
->feed
;
3114 if (stream
->is_feed
) {
3115 for(i
=0;i
<stream
->nb_streams
;i
++) {
3116 stream
->feed_streams
[i
] = i
;
3122 /* create feed files if needed */
3123 for(feed
= first_feed
; feed
!= NULL
; feed
= feed
->next_feed
) {
3126 if (url_exist(feed
->feed_filename
)) {
3127 /* See if it matches */
3131 if (av_open_input_file(&s
, feed
->feed_filename
, NULL
, FFM_PACKET_SIZE
, NULL
) >= 0) {
3132 /* Now see if it matches */
3133 if (s
->nb_streams
== feed
->nb_streams
) {
3135 for(i
=0;i
<s
->nb_streams
;i
++) {
3137 sf
= feed
->streams
[i
];
3140 if (sf
->index
!= ss
->index
||
3142 printf("Index & Id do not match for stream %d\n", i
);
3145 AVCodecContext
*ccf
, *ccs
;
3149 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3151 if (CHECK_CODEC(codec
) || CHECK_CODEC(codec_type
)) {
3152 printf("Codecs do not match for stream %d\n", i
);
3154 } else if (CHECK_CODEC(bit_rate
) || CHECK_CODEC(flags
)) {
3155 printf("Codec bitrates do not match for stream %d\n", i
);
3157 } else if (ccf
->codec_type
== CODEC_TYPE_VIDEO
) {
3158 if (CHECK_CODEC(frame_rate
) ||
3159 CHECK_CODEC(width
) ||
3160 CHECK_CODEC(height
)) {
3161 printf("Codec width, height and framerate do not match for stream %d\n", i
);
3164 } else if (ccf
->codec_type
== CODEC_TYPE_AUDIO
) {
3165 if (CHECK_CODEC(sample_rate
) ||
3166 CHECK_CODEC(channels
) ||
3167 CHECK_CODEC(frame_size
)) {
3168 printf("Codec sample_rate, channels, frame_size do not match for stream %d\n", i
);
3172 printf("Unknown codec type\n");
3181 printf("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3182 feed
->feed_filename
, s
->nb_streams
, feed
->nb_streams
);
3185 av_close_input_file(s
);
3187 printf("Deleting feed file '%s' as it appears to be corrupt\n",
3188 feed
->feed_filename
);
3191 unlink(feed
->feed_filename
);
3193 if (!url_exist(feed
->feed_filename
)) {
3194 AVFormatContext s1
, *s
= &s1
;
3196 /* only write the header of the ffm file */
3197 if (url_fopen(&s
->pb
, feed
->feed_filename
, URL_WRONLY
) < 0) {
3198 fprintf(stderr
, "Could not open output feed file '%s'\n",
3199 feed
->feed_filename
);
3202 s
->oformat
= feed
->fmt
;
3203 s
->nb_streams
= feed
->nb_streams
;
3204 for(i
=0;i
<s
->nb_streams
;i
++) {
3206 st
= feed
->streams
[i
];
3210 /* XXX: need better api */
3211 av_freep(&s
->priv_data
);
3214 /* get feed size and write index */
3215 fd
= open(feed
->feed_filename
, O_RDONLY
);
3217 fprintf(stderr
, "Could not open output feed file '%s'\n",
3218 feed
->feed_filename
);
3222 feed
->feed_write_index
= ffm_read_write_index(fd
);
3223 feed
->feed_size
= lseek(fd
, 0, SEEK_END
);
3224 /* ensure that we do not wrap before the end of file */
3225 if (feed
->feed_max_size
< feed
->feed_size
)
3226 feed
->feed_max_size
= feed
->feed_size
;
3232 static void get_arg(char *buf
, int buf_size
, const char **pp
)
3239 while (isspace(*p
)) p
++;
3242 if (*p
== '\"' || *p
== '\'')
3254 if ((q
- buf
) < buf_size
- 1)
3259 if (quote
&& *p
== quote
)
3264 /* add a codec and set the default parameters */
3265 void add_codec(FFStream
*stream
, AVCodecContext
*av
)
3269 /* compute default parameters */
3270 switch(av
->codec_type
) {
3271 case CODEC_TYPE_AUDIO
:
3272 if (av
->bit_rate
== 0)
3273 av
->bit_rate
= 64000;
3274 if (av
->sample_rate
== 0)
3275 av
->sample_rate
= 22050;
3276 if (av
->channels
== 0)
3279 case CODEC_TYPE_VIDEO
:
3280 if (av
->bit_rate
== 0)
3281 av
->bit_rate
= 64000;
3282 if (av
->frame_rate
== 0)
3283 av
->frame_rate
= 5 * FRAME_RATE_BASE
;
3284 if (av
->width
== 0 || av
->height
== 0) {
3288 /* Bitrate tolerance is less for streaming */
3289 if (av
->bit_rate_tolerance
== 0)
3290 av
->bit_rate_tolerance
= av
->bit_rate
/ 4;
3295 if (av
->max_qdiff
== 0)
3297 av
->qcompress
= 0.5;
3301 av
->rc_eq
= "tex^qComp";
3302 if (!av
->i_quant_factor
)
3303 av
->i_quant_factor
= -0.8;
3304 if (!av
->b_quant_factor
)
3305 av
->b_quant_factor
= 1.25;
3306 if (!av
->b_quant_offset
)
3307 av
->b_quant_offset
= 1.25;
3308 if (!av
->rc_min_rate
)
3309 av
->rc_min_rate
= av
->bit_rate
/ 2;
3310 if (!av
->rc_max_rate
)
3311 av
->rc_max_rate
= av
->bit_rate
* 2;
3318 st
= av_mallocz(sizeof(AVStream
));
3321 stream
->streams
[stream
->nb_streams
++] = st
;
3322 memcpy(&st
->codec
, av
, sizeof(AVCodecContext
));
3325 int opt_audio_codec(const char *arg
)
3331 if (!strcmp(p
->name
, arg
) && p
->type
== CODEC_TYPE_AUDIO
)
3336 return CODEC_ID_NONE
;
3342 int opt_video_codec(const char *arg
)
3348 if (!strcmp(p
->name
, arg
) && p
->type
== CODEC_TYPE_VIDEO
)
3353 return CODEC_ID_NONE
;
3359 /* simplistic plugin support */
3361 void load_module(const char *filename
)
3364 void (*init_func
)(void);
3365 dll
= dlopen(filename
, RTLD_NOW
);
3367 fprintf(stderr
, "Could not load module '%s' - %s\n",
3368 filename
, dlerror());
3372 init_func
= dlsym(dll
, "ffserver_module_init");
3375 "%s: init function 'ffserver_module_init()' not found\n",
3383 int parse_ffconfig(const char *filename
)
3390 int val
, errors
, line_num
;
3391 FFStream
**last_stream
, *stream
, *redirect
;
3392 FFStream
**last_feed
, *feed
;
3393 AVCodecContext audio_enc
, video_enc
;
3394 int audio_id
, video_id
;
3396 f
= fopen(filename
, "r");
3404 first_stream
= NULL
;
3405 last_stream
= &first_stream
;
3407 last_feed
= &first_feed
;
3411 audio_id
= CODEC_ID_NONE
;
3412 video_id
= CODEC_ID_NONE
;
3414 if (fgets(line
, sizeof(line
), f
) == NULL
)
3420 if (*p
== '\0' || *p
== '#')
3423 get_arg(cmd
, sizeof(cmd
), &p
);
3425 if (!strcasecmp(cmd
, "Port")) {
3426 get_arg(arg
, sizeof(arg
), &p
);
3427 my_http_addr
.sin_port
= htons (atoi(arg
));
3428 } else if (!strcasecmp(cmd
, "BindAddress")) {
3429 get_arg(arg
, sizeof(arg
), &p
);
3430 if (!inet_aton(arg
, &my_http_addr
.sin_addr
)) {
3431 fprintf(stderr
, "%s:%d: Invalid IP address: %s\n",
3432 filename
, line_num
, arg
);
3435 } else if (!strcasecmp(cmd
, "NoDaemon")) {
3436 ffserver_daemon
= 0;
3437 } else if (!strcasecmp(cmd
, "RTSPPort")) {
3438 get_arg(arg
, sizeof(arg
), &p
);
3439 my_rtsp_addr
.sin_port
= htons (atoi(arg
));
3440 } else if (!strcasecmp(cmd
, "RTSPBindAddress")) {
3441 get_arg(arg
, sizeof(arg
), &p
);
3442 if (!inet_aton(arg
, &my_rtsp_addr
.sin_addr
)) {
3443 fprintf(stderr
, "%s:%d: Invalid IP address: %s\n",
3444 filename
, line_num
, arg
);
3447 } else if (!strcasecmp(cmd
, "MaxClients")) {
3448 get_arg(arg
, sizeof(arg
), &p
);
3450 if (val
< 1 || val
> HTTP_MAX_CONNECTIONS
) {
3451 fprintf(stderr
, "%s:%d: Invalid MaxClients: %s\n",
3452 filename
, line_num
, arg
);
3455 nb_max_connections
= val
;
3457 } else if (!strcasecmp(cmd
, "MaxBandwidth")) {
3458 get_arg(arg
, sizeof(arg
), &p
);
3460 if (val
< 10 || val
> 100000) {
3461 fprintf(stderr
, "%s:%d: Invalid MaxBandwidth: %s\n",
3462 filename
, line_num
, arg
);
3465 nb_max_bandwidth
= val
;
3467 } else if (!strcasecmp(cmd
, "CustomLog")) {
3468 get_arg(logfilename
, sizeof(logfilename
), &p
);
3469 } else if (!strcasecmp(cmd
, "<Feed")) {
3470 /*********************************************/
3471 /* Feed related options */
3473 if (stream
|| feed
) {
3474 fprintf(stderr
, "%s:%d: Already in a tag\n",
3475 filename
, line_num
);
3477 feed
= av_mallocz(sizeof(FFStream
));
3478 /* add in stream list */
3479 *last_stream
= feed
;
3480 last_stream
= &feed
->next
;
3481 /* add in feed list */
3483 last_feed
= &feed
->next_feed
;
3485 get_arg(feed
->filename
, sizeof(feed
->filename
), &p
);
3486 q
= strrchr(feed
->filename
, '>');
3489 feed
->fmt
= guess_format("ffm", NULL
, NULL
);
3490 /* defaut feed file */
3491 snprintf(feed
->feed_filename
, sizeof(feed
->feed_filename
),
3492 "/tmp/%s.ffm", feed
->filename
);
3493 feed
->feed_max_size
= 5 * 1024 * 1024;
3495 feed
->feed
= feed
; /* self feeding :-) */
3497 } else if (!strcasecmp(cmd
, "Launch")) {
3501 feed
->child_argv
= (char **) av_mallocz(64 * sizeof(char *));
3503 feed
->child_argv
[0] = av_malloc(7);
3504 strcpy(feed
->child_argv
[0], "ffmpeg");
3506 for (i
= 1; i
< 62; i
++) {
3509 get_arg(argbuf
, sizeof(argbuf
), &p
);
3513 feed
->child_argv
[i
] = av_malloc(strlen(argbuf
+ 1));
3514 strcpy(feed
->child_argv
[i
], argbuf
);
3517 feed
->child_argv
[i
] = av_malloc(30 + strlen(feed
->filename
));
3519 snprintf(feed
->child_argv
[i
], 256, "http://127.0.0.1:%d/%s",
3520 ntohs(my_http_addr
.sin_port
), feed
->filename
);
3522 } else if (!strcasecmp(cmd
, "File")) {
3524 get_arg(feed
->feed_filename
, sizeof(feed
->feed_filename
), &p
);
3525 } else if (stream
) {
3526 get_arg(stream
->feed_filename
, sizeof(stream
->feed_filename
), &p
);
3528 } else if (!strcasecmp(cmd
, "FileMaxSize")) {
3533 get_arg(arg
, sizeof(arg
), &p
);
3535 fsize
= strtod(p1
, (char **)&p1
);
3536 switch(toupper(*p1
)) {
3541 fsize
*= 1024 * 1024;
3544 fsize
*= 1024 * 1024 * 1024;
3547 feed
->feed_max_size
= (INT64
)fsize
;
3549 } else if (!strcasecmp(cmd
, "</Feed>")) {
3551 fprintf(stderr
, "%s:%d: No corresponding <Feed> for </Feed>\n",
3552 filename
, line_num
);
3556 /* Make sure that we start out clean */
3557 if (unlink(feed
->feed_filename
) < 0
3558 && errno
!= ENOENT
) {
3559 fprintf(stderr
, "%s:%d: Unable to clean old feed file '%s': %s\n",
3560 filename
, line_num
, feed
->feed_filename
, strerror(errno
));
3566 } else if (!strcasecmp(cmd
, "<Stream")) {
3567 /*********************************************/
3568 /* Stream related options */
3570 if (stream
|| feed
) {
3571 fprintf(stderr
, "%s:%d: Already in a tag\n",
3572 filename
, line_num
);
3574 stream
= av_mallocz(sizeof(FFStream
));
3575 *last_stream
= stream
;
3576 last_stream
= &stream
->next
;
3578 get_arg(stream
->filename
, sizeof(stream
->filename
), &p
);
3579 q
= strrchr(stream
->filename
, '>');
3582 stream
->fmt
= guess_stream_format(NULL
, stream
->filename
, NULL
);
3583 memset(&audio_enc
, 0, sizeof(AVCodecContext
));
3584 memset(&video_enc
, 0, sizeof(AVCodecContext
));
3585 audio_id
= CODEC_ID_NONE
;
3586 video_id
= CODEC_ID_NONE
;
3588 audio_id
= stream
->fmt
->audio_codec
;
3589 video_id
= stream
->fmt
->video_codec
;
3592 } else if (!strcasecmp(cmd
, "Feed")) {
3593 get_arg(arg
, sizeof(arg
), &p
);
3598 while (sfeed
!= NULL
) {
3599 if (!strcmp(sfeed
->filename
, arg
))
3601 sfeed
= sfeed
->next_feed
;
3604 fprintf(stderr
, "%s:%d: feed '%s' not defined\n",
3605 filename
, line_num
, arg
);
3607 stream
->feed
= sfeed
;
3610 } else if (!strcasecmp(cmd
, "Format")) {
3611 get_arg(arg
, sizeof(arg
), &p
);
3612 if (!strcmp(arg
, "status")) {
3613 stream
->stream_type
= STREAM_TYPE_STATUS
;
3616 stream
->stream_type
= STREAM_TYPE_LIVE
;
3617 /* jpeg cannot be used here, so use single frame jpeg */
3618 if (!strcmp(arg
, "jpeg"))
3619 strcpy(arg
, "singlejpeg");
3620 stream
->fmt
= guess_stream_format(arg
, NULL
, NULL
);
3622 fprintf(stderr
, "%s:%d: Unknown Format: %s\n",
3623 filename
, line_num
, arg
);
3628 audio_id
= stream
->fmt
->audio_codec
;
3629 video_id
= stream
->fmt
->video_codec
;
3631 } else if (!strcasecmp(cmd
, "FaviconURL")) {
3632 if (stream
&& stream
->stream_type
== STREAM_TYPE_STATUS
) {
3633 get_arg(stream
->feed_filename
, sizeof(stream
->feed_filename
), &p
);
3635 fprintf(stderr
, "%s:%d: FaviconURL only permitted for status streams\n",
3636 filename
, line_num
);
3639 } else if (!strcasecmp(cmd
, "Author")) {
3641 get_arg(stream
->author
, sizeof(stream
->author
), &p
);
3643 } else if (!strcasecmp(cmd
, "Comment")) {
3645 get_arg(stream
->comment
, sizeof(stream
->comment
), &p
);
3647 } else if (!strcasecmp(cmd
, "Copyright")) {
3649 get_arg(stream
->copyright
, sizeof(stream
->copyright
), &p
);
3651 } else if (!strcasecmp(cmd
, "Title")) {
3653 get_arg(stream
->title
, sizeof(stream
->title
), &p
);
3655 } else if (!strcasecmp(cmd
, "Preroll")) {
3656 get_arg(arg
, sizeof(arg
), &p
);
3658 stream
->prebuffer
= atof(arg
) * 1000;
3660 } else if (!strcasecmp(cmd
, "StartSendOnKey")) {
3662 stream
->send_on_key
= 1;
3664 } else if (!strcasecmp(cmd
, "AudioCodec")) {
3665 get_arg(arg
, sizeof(arg
), &p
);
3666 audio_id
= opt_audio_codec(arg
);
3667 if (audio_id
== CODEC_ID_NONE
) {
3668 fprintf(stderr
, "%s:%d: Unknown AudioCodec: %s\n",
3669 filename
, line_num
, arg
);
3672 } else if (!strcasecmp(cmd
, "VideoCodec")) {
3673 get_arg(arg
, sizeof(arg
), &p
);
3674 video_id
= opt_video_codec(arg
);
3675 if (video_id
== CODEC_ID_NONE
) {
3676 fprintf(stderr
, "%s:%d: Unknown VideoCodec: %s\n",
3677 filename
, line_num
, arg
);
3680 } else if (!strcasecmp(cmd
, "MaxTime")) {
3681 get_arg(arg
, sizeof(arg
), &p
);
3683 stream
->max_time
= atof(arg
) * 1000;
3685 } else if (!strcasecmp(cmd
, "AudioBitRate")) {
3686 get_arg(arg
, sizeof(arg
), &p
);
3688 audio_enc
.bit_rate
= atoi(arg
) * 1000;
3690 } else if (!strcasecmp(cmd
, "AudioChannels")) {
3691 get_arg(arg
, sizeof(arg
), &p
);
3693 audio_enc
.channels
= atoi(arg
);
3695 } else if (!strcasecmp(cmd
, "AudioSampleRate")) {
3696 get_arg(arg
, sizeof(arg
), &p
);
3698 audio_enc
.sample_rate
= atoi(arg
);
3700 } else if (!strcasecmp(cmd
, "AudioQuality")) {
3701 get_arg(arg
, sizeof(arg
), &p
);
3703 audio_enc
.quality
= atof(arg
) * 1000;
3705 } else if (!strcasecmp(cmd
, "VideoBitRateRange")) {
3707 int minrate
, maxrate
;
3709 get_arg(arg
, sizeof(arg
), &p
);
3711 if (sscanf(arg
, "%d-%d", &minrate
, &maxrate
) == 2) {
3712 video_enc
.rc_min_rate
= minrate
* 1000;
3713 video_enc
.rc_max_rate
= maxrate
* 1000;
3715 fprintf(stderr
, "%s:%d: Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n",
3716 filename
, line_num
, arg
);
3720 } else if (!strcasecmp(cmd
, "VideoBitRateTolerance")) {
3722 get_arg(arg
, sizeof(arg
), &p
);
3723 video_enc
.bit_rate_tolerance
= atoi(arg
) * 1000;
3725 } else if (!strcasecmp(cmd
, "VideoBitRate")) {
3726 get_arg(arg
, sizeof(arg
), &p
);
3728 video_enc
.bit_rate
= atoi(arg
) * 1000;
3730 } else if (!strcasecmp(cmd
, "VideoSize")) {
3731 get_arg(arg
, sizeof(arg
), &p
);
3733 parse_image_size(&video_enc
.width
, &video_enc
.height
, arg
);
3734 if ((video_enc
.width
% 16) != 0 ||
3735 (video_enc
.height
% 16) != 0) {
3736 fprintf(stderr
, "%s:%d: Image size must be a multiple of 16\n",
3737 filename
, line_num
);
3741 } else if (!strcasecmp(cmd
, "VideoFrameRate")) {
3742 get_arg(arg
, sizeof(arg
), &p
);
3744 video_enc
.frame_rate
= (int)(strtod(arg
, NULL
) * FRAME_RATE_BASE
);
3746 } else if (!strcasecmp(cmd
, "VideoGopSize")) {
3747 get_arg(arg
, sizeof(arg
), &p
);
3749 video_enc
.gop_size
= atoi(arg
);
3751 } else if (!strcasecmp(cmd
, "VideoIntraOnly")) {
3753 video_enc
.gop_size
= 1;
3755 } else if (!strcasecmp(cmd
, "VideoHighQuality")) {
3757 video_enc
.flags
|= CODEC_FLAG_HQ
;
3759 } else if (!strcasecmp(cmd
, "VideoQDiff")) {
3760 get_arg(arg
, sizeof(arg
), &p
);
3762 video_enc
.max_qdiff
= atoi