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
26 #define closesocket close
31 /* avformat.h defines LIBAVFORMAT_BUILD, include it before all the other libav* headers which use it */
32 #include "libavformat/avformat.h"
33 #include "libavformat/network.h"
34 #include "libavformat/os_support.h"
35 #include "libavformat/rtpdec.h"
36 #include "libavformat/rtsp.h"
37 #include "libavutil/avstring.h"
38 #include "libavutil/lfg.h"
39 #include "libavutil/random_seed.h"
40 #include "libavcodec/opt.h"
44 #include <sys/ioctl.h>
50 #undef time //needed because HAVE_AV_CONFIG_H is defined on top
62 const char program_name
[] = "FFserver";
63 const int program_birth_year
= 2000;
65 static const OptionDef options
[];
68 HTTPSTATE_WAIT_REQUEST
,
69 HTTPSTATE_SEND_HEADER
,
70 HTTPSTATE_SEND_DATA_HEADER
,
71 HTTPSTATE_SEND_DATA
, /* sending TCP or UDP data */
72 HTTPSTATE_SEND_DATA_TRAILER
,
73 HTTPSTATE_RECEIVE_DATA
,
74 HTTPSTATE_WAIT_FEED
, /* wait for data from the feed */
77 RTSPSTATE_WAIT_REQUEST
,
79 RTSPSTATE_SEND_PACKET
,
82 static const char *http_state
[] = {
98 #define IOBUFFER_INIT_SIZE 8192
100 /* timeouts are in ms */
101 #define HTTP_REQUEST_TIMEOUT (15 * 1000)
102 #define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
104 #define SYNC_TIMEOUT (10 * 1000)
106 typedef struct RTSPActionServerSetup
{
108 char transport_option
[512];
109 } RTSPActionServerSetup
;
112 int64_t count1
, count2
;
113 int64_t time1
, time2
;
116 /* context associated with one connection */
117 typedef struct HTTPContext
{
118 enum HTTPState state
;
119 int fd
; /* socket file descriptor */
120 struct sockaddr_in from_addr
; /* origin */
121 struct pollfd
*poll_entry
; /* used when polling */
123 uint8_t *buffer_ptr
, *buffer_end
;
126 int chunked_encoding
;
127 int chunk_size
; /* 0 if it needs to be read */
128 struct HTTPContext
*next
;
129 int got_key_frame
; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
133 /* input format handling */
134 AVFormatContext
*fmt_in
;
135 int64_t start_time
; /* In milliseconds - this wraps fairly often */
136 int64_t first_pts
; /* initial pts value */
137 int64_t cur_pts
; /* current pts value from the stream in us */
138 int64_t cur_frame_duration
; /* duration of the current frame in us */
139 int cur_frame_bytes
; /* output frame size, needed to compute
140 the time at which we send each
142 int pts_stream_index
; /* stream we choose as clock reference */
143 int64_t cur_clock
; /* current clock reference value in us */
144 /* output format handling */
145 struct FFStream
*stream
;
146 /* -1 is invalid stream */
147 int feed_streams
[MAX_STREAMS
]; /* index of streams in the feed */
148 int switch_feed_streams
[MAX_STREAMS
]; /* index of streams in the feed */
150 AVFormatContext fmt_ctx
; /* instance of FFStream for one user */
151 int last_packet_sent
; /* true if last data packet was sent */
153 DataRateData datarate
;
160 int is_packetized
; /* if true, the stream is packetized */
161 int packet_stream_index
; /* current stream for output in state machine */
163 /* RTSP state specific */
164 uint8_t *pb_buffer
; /* XXX: use that in all the code */
166 int seq
; /* RTSP sequence number */
168 /* RTP state specific */
169 enum RTSPLowerTransport rtp_protocol
;
170 char session_id
[32]; /* session id */
171 AVFormatContext
*rtp_ctx
[MAX_STREAMS
];
173 /* RTP/UDP specific */
174 URLContext
*rtp_handles
[MAX_STREAMS
];
176 /* RTP/TCP specific */
177 struct HTTPContext
*rtsp_c
;
178 uint8_t *packet_buffer
, *packet_buffer_ptr
, *packet_buffer_end
;
181 /* each generated stream is described here */
185 STREAM_TYPE_REDIRECT
,
188 enum IPAddressAction
{
193 typedef struct IPAddressACL
{
194 struct IPAddressACL
*next
;
195 enum IPAddressAction action
;
196 /* These are in host order */
197 struct in_addr first
;
201 /* description of each stream of the ffserver.conf file */
202 typedef struct FFStream
{
203 enum StreamType stream_type
;
204 char filename
[1024]; /* stream filename */
205 struct FFStream
*feed
; /* feed we are using (can be null if
207 AVFormatParameters
*ap_in
; /* input parameters */
208 AVInputFormat
*ifmt
; /* if non NULL, force input format */
212 int prebuffer
; /* Number of millseconds early to start */
213 int64_t max_time
; /* Number of milliseconds to run */
215 AVStream
*streams
[MAX_STREAMS
];
216 int feed_streams
[MAX_STREAMS
]; /* index of streams in the feed */
217 char feed_filename
[1024]; /* file name of the feed storage, or
218 input file name for a stream */
223 pid_t pid
; /* Of ffmpeg process */
224 time_t pid_start
; /* Of ffmpeg process */
226 struct FFStream
*next
;
227 unsigned bandwidth
; /* bandwidth, in kbits/s */
230 /* multicast specific */
232 struct in_addr multicast_ip
;
233 int multicast_port
; /* first port used for multicast */
235 int loop
; /* if true, send the stream in loops (only meaningful if file) */
238 int feed_opened
; /* true if someone is writing to the feed */
239 int is_feed
; /* true if it is a feed */
240 int readonly
; /* True if writing is prohibited to the file */
241 int truncate
; /* True if feeder connection truncate the feed file */
243 int64_t bytes_served
;
244 int64_t feed_max_size
; /* maximum storage size, zero means unlimited */
245 int64_t feed_write_index
; /* current write position in feed (it wraps around) */
246 int64_t feed_size
; /* current size of feed */
247 struct FFStream
*next_feed
;
250 typedef struct FeedData
{
251 long long data_count
;
252 float avg_frame_size
; /* frame size averaged over last frames with exponential mean */
255 static struct sockaddr_in my_http_addr
;
256 static struct sockaddr_in my_rtsp_addr
;
258 static char logfilename
[1024];
259 static HTTPContext
*first_http_ctx
;
260 static FFStream
*first_feed
; /* contains only feeds */
261 static FFStream
*first_stream
; /* contains all streams, including feeds */
263 static void new_connection(int server_fd
, int is_rtsp
);
264 static void close_connection(HTTPContext
*c
);
267 static int handle_connection(HTTPContext
*c
);
268 static int http_parse_request(HTTPContext
*c
);
269 static int http_send_data(HTTPContext
*c
);
270 static void compute_status(HTTPContext
*c
);
271 static int open_input_stream(HTTPContext
*c
, const char *info
);
272 static int http_start_receive_data(HTTPContext
*c
);
273 static int http_receive_data(HTTPContext
*c
);
276 static int rtsp_parse_request(HTTPContext
*c
);
277 static void rtsp_cmd_describe(HTTPContext
*c
, const char *url
);
278 static void rtsp_cmd_options(HTTPContext
*c
, const char *url
);
279 static void rtsp_cmd_setup(HTTPContext
*c
, const char *url
, RTSPMessageHeader
*h
);
280 static void rtsp_cmd_play(HTTPContext
*c
, const char *url
, RTSPMessageHeader
*h
);
281 static void rtsp_cmd_pause(HTTPContext
*c
, const char *url
, RTSPMessageHeader
*h
);
282 static void rtsp_cmd_teardown(HTTPContext
*c
, const char *url
, RTSPMessageHeader
*h
);
285 static int prepare_sdp_description(FFStream
*stream
, uint8_t **pbuffer
,
286 struct in_addr my_ip
);
289 static HTTPContext
*rtp_new_connection(struct sockaddr_in
*from_addr
,
290 FFStream
*stream
, const char *session_id
,
291 enum RTSPLowerTransport rtp_protocol
);
292 static int rtp_new_av_stream(HTTPContext
*c
,
293 int stream_index
, struct sockaddr_in
*dest_addr
,
294 HTTPContext
*rtsp_c
);
296 static const char *my_program_name
;
297 static const char *my_program_dir
;
299 static const char *config_filename
;
300 static int ffserver_debug
;
301 static int ffserver_daemon
;
302 static int no_launch
;
303 static int need_to_start_children
;
305 /* maximum number of simultaneous HTTP connections */
306 static unsigned int nb_max_http_connections
= 2000;
307 static unsigned int nb_max_connections
= 5;
308 static unsigned int nb_connections
;
310 static uint64_t max_bandwidth
= 1000;
311 static uint64_t current_bandwidth
;
313 static int64_t cur_time
; // Making this global saves on passing it around everywhere
315 static AVLFG random_state
;
317 static FILE *logfile
= NULL
;
319 static char *ctime1(char *buf2
)
327 p
= buf2
+ strlen(p
) - 1;
333 static void http_vlog(const char *fmt
, va_list vargs
)
335 static int print_prefix
= 1;
340 fprintf(logfile
, "%s ", buf
);
342 print_prefix
= strstr(fmt
, "\n") != NULL
;
343 vfprintf(logfile
, fmt
, vargs
);
348 static void __attribute__ ((format (printf
, 1, 2))) http_log(const char *fmt
, ...)
351 va_start(vargs
, fmt
);
352 http_vlog(fmt
, vargs
);
356 static void http_av_log(void *ptr
, int level
, const char *fmt
, va_list vargs
)
358 static int print_prefix
= 1;
359 AVClass
*avc
= ptr ?
*(AVClass
**)ptr
: NULL
;
360 if (level
> av_log_get_level())
362 if (print_prefix
&& avc
)
363 http_log("[%s @ %p]", avc
->item_name(ptr
), ptr
);
364 print_prefix
= strstr(fmt
, "\n") != NULL
;
365 http_vlog(fmt
, vargs
);
368 static void log_connection(HTTPContext
*c
)
373 http_log("%s - - [%s] \"%s %s\" %d %"PRId64
"\n",
374 inet_ntoa(c
->from_addr
.sin_addr
), c
->method
, c
->url
,
375 c
->protocol
, (c
->http_error ? c
->http_error
: 200), c
->data_count
);
378 static void update_datarate(DataRateData
*drd
, int64_t count
)
380 if (!drd
->time1
&& !drd
->count1
) {
381 drd
->time1
= drd
->time2
= cur_time
;
382 drd
->count1
= drd
->count2
= count
;
383 } else if (cur_time
- drd
->time2
> 5000) {
384 drd
->time1
= drd
->time2
;
385 drd
->count1
= drd
->count2
;
386 drd
->time2
= cur_time
;
391 /* In bytes per second */
392 static int compute_datarate(DataRateData
*drd
, int64_t count
)
394 if (cur_time
== drd
->time1
)
397 return ((count
- drd
->count1
) * 1000) / (cur_time
- drd
->time1
);
401 static void start_children(FFStream
*feed
)
406 for (; feed
; feed
= feed
->next
) {
407 if (feed
->child_argv
&& !feed
->pid
) {
408 feed
->pid_start
= time(0);
413 http_log("Unable to create children\n");
422 av_strlcpy(pathname
, my_program_name
, sizeof(pathname
));
424 slash
= strrchr(pathname
, '/');
429 strcpy(slash
, "ffmpeg");
431 http_log("Launch commandline: ");
432 http_log("%s ", pathname
);
433 for (i
= 1; feed
->child_argv
[i
] && feed
->child_argv
[i
][0]; i
++)
434 http_log("%s ", feed
->child_argv
[i
]);
437 for (i
= 3; i
< 256; i
++)
440 if (!ffserver_debug
) {
441 i
= open("/dev/null", O_RDWR
);
450 /* This is needed to make relative pathnames work */
451 chdir(my_program_dir
);
453 signal(SIGPIPE
, SIG_DFL
);
455 execvp(pathname
, feed
->child_argv
);
463 /* open a listening socket */
464 static int socket_open_listen(struct sockaddr_in
*my_addr
)
468 server_fd
= socket(AF_INET
,SOCK_STREAM
,0);
475 setsockopt(server_fd
, SOL_SOCKET
, SO_REUSEADDR
, &tmp
, sizeof(tmp
));
477 if (bind (server_fd
, (struct sockaddr
*) my_addr
, sizeof (*my_addr
)) < 0) {
479 snprintf(bindmsg
, sizeof(bindmsg
), "bind(port %d)", ntohs(my_addr
->sin_port
));
481 closesocket(server_fd
);
485 if (listen (server_fd
, 5) < 0) {
487 closesocket(server_fd
);
490 ff_socket_nonblock(server_fd
, 1);
495 /* start all multicast streams */
496 static void start_multicast(void)
501 struct sockaddr_in dest_addr
;
502 int default_port
, stream_index
;
505 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
506 if (stream
->is_multicast
) {
507 /* open the RTP connection */
508 snprintf(session_id
, sizeof(session_id
), "%08x%08x",
509 av_lfg_get(&random_state
), av_lfg_get(&random_state
));
511 /* choose a port if none given */
512 if (stream
->multicast_port
== 0) {
513 stream
->multicast_port
= default_port
;
517 dest_addr
.sin_family
= AF_INET
;
518 dest_addr
.sin_addr
= stream
->multicast_ip
;
519 dest_addr
.sin_port
= htons(stream
->multicast_port
);
521 rtp_c
= rtp_new_connection(&dest_addr
, stream
, session_id
,
522 RTSP_LOWER_TRANSPORT_UDP_MULTICAST
);
526 if (open_input_stream(rtp_c
, "") < 0) {
527 http_log("Could not open input stream for stream '%s'\n",
532 /* open each RTP stream */
533 for(stream_index
= 0; stream_index
< stream
->nb_streams
;
535 dest_addr
.sin_port
= htons(stream
->multicast_port
+
537 if (rtp_new_av_stream(rtp_c
, stream_index
, &dest_addr
, NULL
) < 0) {
538 http_log("Could not open output stream '%s/streamid=%d'\n",
539 stream
->filename
, stream_index
);
544 /* change state to send data */
545 rtp_c
->state
= HTTPSTATE_SEND_DATA
;
550 /* main loop of the http server */
551 static int http_server(void)
553 int server_fd
= 0, rtsp_server_fd
= 0;
554 int ret
, delay
, delay1
;
555 struct pollfd
*poll_table
, *poll_entry
;
556 HTTPContext
*c
, *c_next
;
558 if(!(poll_table
= av_mallocz((nb_max_http_connections
+ 2)*sizeof(*poll_table
)))) {
559 http_log("Impossible to allocate a poll table handling %d connections.\n", nb_max_http_connections
);
563 if (my_http_addr
.sin_port
) {
564 server_fd
= socket_open_listen(&my_http_addr
);
569 if (my_rtsp_addr
.sin_port
) {
570 rtsp_server_fd
= socket_open_listen(&my_rtsp_addr
);
571 if (rtsp_server_fd
< 0)
575 if (!rtsp_server_fd
&& !server_fd
) {
576 http_log("HTTP and RTSP disabled.\n");
580 http_log("FFserver started.\n");
582 start_children(first_feed
);
587 poll_entry
= poll_table
;
589 poll_entry
->fd
= server_fd
;
590 poll_entry
->events
= POLLIN
;
593 if (rtsp_server_fd
) {
594 poll_entry
->fd
= rtsp_server_fd
;
595 poll_entry
->events
= POLLIN
;
599 /* wait for events on each HTTP handle */
606 case HTTPSTATE_SEND_HEADER
:
607 case RTSPSTATE_SEND_REPLY
:
608 case RTSPSTATE_SEND_PACKET
:
609 c
->poll_entry
= poll_entry
;
611 poll_entry
->events
= POLLOUT
;
614 case HTTPSTATE_SEND_DATA_HEADER
:
615 case HTTPSTATE_SEND_DATA
:
616 case HTTPSTATE_SEND_DATA_TRAILER
:
617 if (!c
->is_packetized
) {
618 /* for TCP, we output as much as we can (may need to put a limit) */
619 c
->poll_entry
= poll_entry
;
621 poll_entry
->events
= POLLOUT
;
624 /* when ffserver is doing the timing, we work by
625 looking at which packet need to be sent every
627 delay1
= 10; /* one tick wait XXX: 10 ms assumed */
632 case HTTPSTATE_WAIT_REQUEST
:
633 case HTTPSTATE_RECEIVE_DATA
:
634 case HTTPSTATE_WAIT_FEED
:
635 case RTSPSTATE_WAIT_REQUEST
:
636 /* need to catch errors */
637 c
->poll_entry
= poll_entry
;
639 poll_entry
->events
= POLLIN
;/* Maybe this will work */
643 c
->poll_entry
= NULL
;
649 /* wait for an event on one connection. We poll at least every
650 second to handle timeouts */
652 ret
= poll(poll_table
, poll_entry
- poll_table
, delay
);
653 if (ret
< 0 && ff_neterrno() != FF_NETERROR(EAGAIN
) &&
654 ff_neterrno() != FF_NETERROR(EINTR
))
658 cur_time
= av_gettime() / 1000;
660 if (need_to_start_children
) {
661 need_to_start_children
= 0;
662 start_children(first_feed
);
665 /* now handle the events */
666 for(c
= first_http_ctx
; c
!= NULL
; c
= c_next
) {
668 if (handle_connection(c
) < 0) {
669 /* close and free the connection */
675 poll_entry
= poll_table
;
677 /* new HTTP connection request ? */
678 if (poll_entry
->revents
& POLLIN
)
679 new_connection(server_fd
, 0);
682 if (rtsp_server_fd
) {
683 /* new RTSP connection request ? */
684 if (poll_entry
->revents
& POLLIN
)
685 new_connection(rtsp_server_fd
, 1);
690 /* start waiting for a new HTTP/RTSP request */
691 static void start_wait_request(HTTPContext
*c
, int is_rtsp
)
693 c
->buffer_ptr
= c
->buffer
;
694 c
->buffer_end
= c
->buffer
+ c
->buffer_size
- 1; /* leave room for '\0' */
697 c
->timeout
= cur_time
+ RTSP_REQUEST_TIMEOUT
;
698 c
->state
= RTSPSTATE_WAIT_REQUEST
;
700 c
->timeout
= cur_time
+ HTTP_REQUEST_TIMEOUT
;
701 c
->state
= HTTPSTATE_WAIT_REQUEST
;
705 static void http_send_too_busy_reply(int fd
)
708 int len
= snprintf(buffer
, sizeof(buffer
),
709 "HTTP/1.0 200 Server too busy\r\n"
710 "Content-type: text/html\r\n"
712 "<html><head><title>Too busy</title></head><body>\r\n"
713 "<p>The server is too busy to serve your request at this time.</p>\r\n"
714 "<p>The number of current connections is %d, and this exceeds the limit of %d.</p>\r\n"
715 "</body></html>\r\n",
716 nb_connections
, nb_max_connections
);
717 send(fd
, buffer
, len
, 0);
721 static void new_connection(int server_fd
, int is_rtsp
)
723 struct sockaddr_in from_addr
;
725 HTTPContext
*c
= NULL
;
727 len
= sizeof(from_addr
);
728 fd
= accept(server_fd
, (struct sockaddr
*)&from_addr
,
731 http_log("error during accept %s\n", strerror(errno
));
734 ff_socket_nonblock(fd
, 1);
736 if (nb_connections
>= nb_max_connections
) {
737 http_send_too_busy_reply(fd
);
741 /* add a new connection */
742 c
= av_mallocz(sizeof(HTTPContext
));
747 c
->poll_entry
= NULL
;
748 c
->from_addr
= from_addr
;
749 c
->buffer_size
= IOBUFFER_INIT_SIZE
;
750 c
->buffer
= av_malloc(c
->buffer_size
);
754 c
->next
= first_http_ctx
;
758 start_wait_request(c
, is_rtsp
);
770 static void close_connection(HTTPContext
*c
)
772 HTTPContext
**cp
, *c1
;
774 AVFormatContext
*ctx
;
778 /* remove connection from list */
779 cp
= &first_http_ctx
;
780 while ((*cp
) != NULL
) {
788 /* remove references, if any (XXX: do it faster) */
789 for(c1
= first_http_ctx
; c1
!= NULL
; c1
= c1
->next
) {
794 /* remove connection associated resources */
798 /* close each frame parser */
799 for(i
=0;i
<c
->fmt_in
->nb_streams
;i
++) {
800 st
= c
->fmt_in
->streams
[i
];
801 if (st
->codec
->codec
)
802 avcodec_close(st
->codec
);
804 av_close_input_file(c
->fmt_in
);
807 /* free RTP output streams if any */
810 nb_streams
= c
->stream
->nb_streams
;
812 for(i
=0;i
<nb_streams
;i
++) {
815 av_write_trailer(ctx
);
818 h
= c
->rtp_handles
[i
];
825 if (!c
->last_packet_sent
&& c
->state
== HTTPSTATE_SEND_DATA_TRAILER
) {
828 if (url_open_dyn_buf(&ctx
->pb
) >= 0) {
829 av_write_trailer(ctx
);
830 av_freep(&c
->pb_buffer
);
831 url_close_dyn_buf(ctx
->pb
, &c
->pb_buffer
);
836 for(i
=0; i
<ctx
->nb_streams
; i
++)
837 av_free(ctx
->streams
[i
]);
839 if (c
->stream
&& !c
->post
&& c
->stream
->stream_type
== STREAM_TYPE_LIVE
)
840 current_bandwidth
-= c
->stream
->bandwidth
;
842 /* signal that there is no feed if we are the feeder socket */
843 if (c
->state
== HTTPSTATE_RECEIVE_DATA
&& c
->stream
) {
844 c
->stream
->feed_opened
= 0;
848 av_freep(&c
->pb_buffer
);
849 av_freep(&c
->packet_buffer
);
855 static int handle_connection(HTTPContext
*c
)
860 case HTTPSTATE_WAIT_REQUEST
:
861 case RTSPSTATE_WAIT_REQUEST
:
863 if ((c
->timeout
- cur_time
) < 0)
865 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
))
868 /* no need to read if no events */
869 if (!(c
->poll_entry
->revents
& POLLIN
))
873 len
= recv(c
->fd
, c
->buffer_ptr
, 1, 0);
875 if (ff_neterrno() != FF_NETERROR(EAGAIN
) &&
876 ff_neterrno() != FF_NETERROR(EINTR
))
878 } else if (len
== 0) {
881 /* search for end of request. */
883 c
->buffer_ptr
+= len
;
885 if ((ptr
>= c
->buffer
+ 2 && !memcmp(ptr
-2, "\n\n", 2)) ||
886 (ptr
>= c
->buffer
+ 4 && !memcmp(ptr
-4, "\r\n\r\n", 4))) {
887 /* request found : parse it and reply */
888 if (c
->state
== HTTPSTATE_WAIT_REQUEST
) {
889 ret
= http_parse_request(c
);
891 ret
= rtsp_parse_request(c
);
895 } else if (ptr
>= c
->buffer_end
) {
896 /* request too long: cannot do anything */
898 } else goto read_loop
;
902 case HTTPSTATE_SEND_HEADER
:
903 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
))
906 /* no need to write if no events */
907 if (!(c
->poll_entry
->revents
& POLLOUT
))
909 len
= send(c
->fd
, c
->buffer_ptr
, c
->buffer_end
- c
->buffer_ptr
, 0);
911 if (ff_neterrno() != FF_NETERROR(EAGAIN
) &&
912 ff_neterrno() != FF_NETERROR(EINTR
)) {
913 /* error : close connection */
914 av_freep(&c
->pb_buffer
);
918 c
->buffer_ptr
+= len
;
920 c
->stream
->bytes_served
+= len
;
921 c
->data_count
+= len
;
922 if (c
->buffer_ptr
>= c
->buffer_end
) {
923 av_freep(&c
->pb_buffer
);
927 /* all the buffer was sent : synchronize to the incoming stream */
928 c
->state
= HTTPSTATE_SEND_DATA_HEADER
;
929 c
->buffer_ptr
= c
->buffer_end
= c
->buffer
;
934 case HTTPSTATE_SEND_DATA
:
935 case HTTPSTATE_SEND_DATA_HEADER
:
936 case HTTPSTATE_SEND_DATA_TRAILER
:
937 /* for packetized output, we consider we can always write (the
938 input streams sets the speed). It may be better to verify
939 that we do not rely too much on the kernel queues */
940 if (!c
->is_packetized
) {
941 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
))
944 /* no need to read if no events */
945 if (!(c
->poll_entry
->revents
& POLLOUT
))
948 if (http_send_data(c
) < 0)
950 /* close connection if trailer sent */
951 if (c
->state
== HTTPSTATE_SEND_DATA_TRAILER
)
954 case HTTPSTATE_RECEIVE_DATA
:
955 /* no need to read if no events */
956 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
))
958 if (!(c
->poll_entry
->revents
& POLLIN
))
960 if (http_receive_data(c
) < 0)
963 case HTTPSTATE_WAIT_FEED
:
964 /* no need to read if no events */
965 if (c
->poll_entry
->revents
& (POLLIN
| POLLERR
| POLLHUP
))
968 /* nothing to do, we'll be waken up by incoming feed packets */
971 case RTSPSTATE_SEND_REPLY
:
972 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
)) {
973 av_freep(&c
->pb_buffer
);
976 /* no need to write if no events */
977 if (!(c
->poll_entry
->revents
& POLLOUT
))
979 len
= send(c
->fd
, c
->buffer_ptr
, c
->buffer_end
- c
->buffer_ptr
, 0);
981 if (ff_neterrno() != FF_NETERROR(EAGAIN
) &&
982 ff_neterrno() != FF_NETERROR(EINTR
)) {
983 /* error : close connection */
984 av_freep(&c
->pb_buffer
);
988 c
->buffer_ptr
+= len
;
989 c
->data_count
+= len
;
990 if (c
->buffer_ptr
>= c
->buffer_end
) {
991 /* all the buffer was sent : wait for a new request */
992 av_freep(&c
->pb_buffer
);
993 start_wait_request(c
, 1);
997 case RTSPSTATE_SEND_PACKET
:
998 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
)) {
999 av_freep(&c
->packet_buffer
);
1002 /* no need to write if no events */
1003 if (!(c
->poll_entry
->revents
& POLLOUT
))
1005 len
= send(c
->fd
, c
->packet_buffer_ptr
,
1006 c
->packet_buffer_end
- c
->packet_buffer_ptr
, 0);
1008 if (ff_neterrno() != FF_NETERROR(EAGAIN
) &&
1009 ff_neterrno() != FF_NETERROR(EINTR
)) {
1010 /* error : close connection */
1011 av_freep(&c
->packet_buffer
);
1015 c
->packet_buffer_ptr
+= len
;
1016 if (c
->packet_buffer_ptr
>= c
->packet_buffer_end
) {
1017 /* all the buffer was sent : wait for a new request */
1018 av_freep(&c
->packet_buffer
);
1019 c
->state
= RTSPSTATE_WAIT_REQUEST
;
1023 case HTTPSTATE_READY
:
1032 static int extract_rates(char *rates
, int ratelen
, const char *request
)
1036 for (p
= request
; *p
&& *p
!= '\r' && *p
!= '\n'; ) {
1037 if (strncasecmp(p
, "Pragma:", 7) == 0) {
1038 const char *q
= p
+ 7;
1040 while (*q
&& *q
!= '\n' && isspace(*q
))
1043 if (strncasecmp(q
, "stream-switch-entry=", 20) == 0) {
1049 memset(rates
, 0xff, ratelen
);
1052 while (*q
&& *q
!= '\n' && *q
!= ':')
1055 if (sscanf(q
, ":%d:%d", &stream_no
, &rate_no
) != 2)
1059 if (stream_no
< ratelen
&& stream_no
>= 0)
1060 rates
[stream_no
] = rate_no
;
1062 while (*q
&& *q
!= '\n' && !isspace(*q
))
1069 p
= strchr(p
, '\n');
1079 static int find_stream_in_feed(FFStream
*feed
, AVCodecContext
*codec
, int bit_rate
)
1082 int best_bitrate
= 100000000;
1085 for (i
= 0; i
< feed
->nb_streams
; i
++) {
1086 AVCodecContext
*feed_codec
= feed
->streams
[i
]->codec
;
1088 if (feed_codec
->codec_id
!= codec
->codec_id
||
1089 feed_codec
->sample_rate
!= codec
->sample_rate
||
1090 feed_codec
->width
!= codec
->width
||
1091 feed_codec
->height
!= codec
->height
)
1094 /* Potential stream */
1096 /* We want the fastest stream less than bit_rate, or the slowest
1097 * faster than bit_rate
1100 if (feed_codec
->bit_rate
<= bit_rate
) {
1101 if (best_bitrate
> bit_rate
|| feed_codec
->bit_rate
> best_bitrate
) {
1102 best_bitrate
= feed_codec
->bit_rate
;
1106 if (feed_codec
->bit_rate
< best_bitrate
) {
1107 best_bitrate
= feed_codec
->bit_rate
;
1116 static int modify_current_stream(HTTPContext
*c
, char *rates
)
1119 FFStream
*req
= c
->stream
;
1120 int action_required
= 0;
1122 /* Not much we can do for a feed */
1126 for (i
= 0; i
< req
->nb_streams
; i
++) {
1127 AVCodecContext
*codec
= req
->streams
[i
]->codec
;
1131 c
->switch_feed_streams
[i
] = req
->feed_streams
[i
];
1134 c
->switch_feed_streams
[i
] = find_stream_in_feed(req
->feed
, codec
, codec
->bit_rate
/ 2);
1137 /* Wants off or slow */
1138 c
->switch_feed_streams
[i
] = find_stream_in_feed(req
->feed
, codec
, codec
->bit_rate
/ 4);
1140 /* This doesn't work well when it turns off the only stream! */
1141 c
->switch_feed_streams
[i
] = -2;
1142 c
->feed_streams
[i
] = -2;
1147 if (c
->switch_feed_streams
[i
] >= 0 && c
->switch_feed_streams
[i
] != c
->feed_streams
[i
])
1148 action_required
= 1;
1151 return action_required
;
1155 static void do_switch_stream(HTTPContext
*c
, int i
)
1157 if (c
->switch_feed_streams
[i
] >= 0) {
1159 c
->feed_streams
[i
] = c
->switch_feed_streams
[i
];
1162 /* Now update the stream */
1164 c
->switch_feed_streams
[i
] = -1;
1167 /* XXX: factorize in utils.c ? */
1168 /* XXX: take care with different space meaning */
1169 static void skip_spaces(const char **pp
)
1173 while (*p
== ' ' || *p
== '\t')
1178 static void get_word(char *buf
, int buf_size
, const char **pp
)
1186 while (!isspace(*p
) && *p
!= '\0') {
1187 if ((q
- buf
) < buf_size
- 1)
1196 static void get_arg(char *buf
, int buf_size
, const char **pp
)
1203 while (isspace(*p
)) p
++;
1206 if (*p
== '\"' || *p
== '\'')
1218 if ((q
- buf
) < buf_size
- 1)
1223 if (quote
&& *p
== quote
)
1228 static int validate_acl(FFStream
*stream
, HTTPContext
*c
)
1230 enum IPAddressAction last_action
= IP_DENY
;
1232 struct in_addr
*src
= &c
->from_addr
.sin_addr
;
1233 unsigned long src_addr
= src
->s_addr
;
1235 for (acl
= stream
->acl
; acl
; acl
= acl
->next
) {
1236 if (src_addr
>= acl
->first
.s_addr
&& src_addr
<= acl
->last
.s_addr
)
1237 return (acl
->action
== IP_ALLOW
) ?
1 : 0;
1238 last_action
= acl
->action
;
1241 /* Nothing matched, so return not the last action */
1242 return (last_action
== IP_DENY
) ?
1 : 0;
1245 /* compute the real filename of a file by matching it without its
1246 extensions to all the stream filenames */
1247 static void compute_real_filename(char *filename
, int max_size
)
1254 /* compute filename by matching without the file extensions */
1255 av_strlcpy(file1
, filename
, sizeof(file1
));
1256 p
= strrchr(file1
, '.');
1259 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
1260 av_strlcpy(file2
, stream
->filename
, sizeof(file2
));
1261 p
= strrchr(file2
, '.');
1264 if (!strcmp(file1
, file2
)) {
1265 av_strlcpy(filename
, stream
->filename
, max_size
);
1280 /* parse http request and prepare header */
1281 static int http_parse_request(HTTPContext
*c
)
1284 enum RedirType redir_type
;
1286 char info
[1024], filename
[1024];
1290 const char *mime_type
;
1294 char *useragent
= 0;
1297 get_word(cmd
, sizeof(cmd
), (const char **)&p
);
1298 av_strlcpy(c
->method
, cmd
, sizeof(c
->method
));
1300 if (!strcmp(cmd
, "GET"))
1302 else if (!strcmp(cmd
, "POST"))
1307 get_word(url
, sizeof(url
), (const char **)&p
);
1308 av_strlcpy(c
->url
, url
, sizeof(c
->url
));
1310 get_word(protocol
, sizeof(protocol
), (const char **)&p
);
1311 if (strcmp(protocol
, "HTTP/1.0") && strcmp(protocol
, "HTTP/1.1"))
1314 av_strlcpy(c
->protocol
, protocol
, sizeof(c
->protocol
));
1317 http_log("%s - - New connection: %s %s\n", inet_ntoa(c
->from_addr
.sin_addr
), cmd
, url
);
1319 /* find the filename and the optional info string in the request */
1320 p
= strchr(url
, '?');
1322 av_strlcpy(info
, p
, sizeof(info
));
1327 av_strlcpy(filename
, url
+ ((*url
== '/') ?
1 : 0), sizeof(filename
)-1);
1329 for (p
= c
->buffer
; *p
&& *p
!= '\r' && *p
!= '\n'; ) {
1330 if (strncasecmp(p
, "User-Agent:", 11) == 0) {
1332 if (*useragent
&& *useragent
!= '\n' && isspace(*useragent
))
1336 p
= strchr(p
, '\n');
1343 redir_type
= REDIR_NONE
;
1344 if (av_match_ext(filename
, "asx")) {
1345 redir_type
= REDIR_ASX
;
1346 filename
[strlen(filename
)-1] = 'f';
1347 } else if (av_match_ext(filename
, "asf") &&
1348 (!useragent
|| strncasecmp(useragent
, "NSPlayer", 8) != 0)) {
1349 /* if this isn't WMP or lookalike, return the redirector file */
1350 redir_type
= REDIR_ASF
;
1351 } else if (av_match_ext(filename
, "rpm,ram")) {
1352 redir_type
= REDIR_RAM
;
1353 strcpy(filename
+ strlen(filename
)-2, "m");
1354 } else if (av_match_ext(filename
, "rtsp")) {
1355 redir_type
= REDIR_RTSP
;
1356 compute_real_filename(filename
, sizeof(filename
) - 1);
1357 } else if (av_match_ext(filename
, "sdp")) {
1358 redir_type
= REDIR_SDP
;
1359 compute_real_filename(filename
, sizeof(filename
) - 1);
1362 // "redirect" / request to index.html
1363 if (!strlen(filename
))
1364 av_strlcpy(filename
, "index.html", sizeof(filename
) - 1);
1366 stream
= first_stream
;
1367 while (stream
!= NULL
) {
1368 if (!strcmp(stream
->filename
, filename
) && validate_acl(stream
, c
))
1370 stream
= stream
->next
;
1372 if (stream
== NULL
) {
1373 snprintf(msg
, sizeof(msg
), "File '%s' not found", url
);
1374 http_log("File '%s' not found\n", url
);
1379 memcpy(c
->feed_streams
, stream
->feed_streams
, sizeof(c
->feed_streams
));
1380 memset(c
->switch_feed_streams
, -1, sizeof(c
->switch_feed_streams
));
1382 if (stream
->stream_type
== STREAM_TYPE_REDIRECT
) {
1383 c
->http_error
= 301;
1385 q
+= snprintf(q
, c
->buffer_size
,
1386 "HTTP/1.0 301 Moved\r\n"
1388 "Content-type: text/html\r\n"
1390 "<html><head><title>Moved</title></head><body>\r\n"
1391 "You should be <a href=\"%s\">redirected</a>.\r\n"
1392 "</body></html>\r\n", stream
->feed_filename
, stream
->feed_filename
);
1393 /* prepare output buffer */
1394 c
->buffer_ptr
= c
->buffer
;
1396 c
->state
= HTTPSTATE_SEND_HEADER
;
1400 /* If this is WMP, get the rate information */
1401 if (extract_rates(ratebuf
, sizeof(ratebuf
), c
->buffer
)) {
1402 if (modify_current_stream(c
, ratebuf
)) {
1403 for (i
= 0; i
< FF_ARRAY_ELEMS(c
->feed_streams
); i
++) {
1404 if (c
->switch_feed_streams
[i
] >= 0)
1405 do_switch_stream(c
, i
);
1410 if (c
->post
== 0 && stream
->stream_type
== STREAM_TYPE_LIVE
)
1411 current_bandwidth
+= stream
->bandwidth
;
1413 /* If already streaming this feed, do not let start another feeder. */
1414 if (stream
->feed_opened
) {
1415 snprintf(msg
, sizeof(msg
), "This feed is already being received.");
1416 http_log("Feed '%s' already being received\n", stream
->feed_filename
);
1420 if (c
->post
== 0 && max_bandwidth
< current_bandwidth
) {
1421 c
->http_error
= 200;
1423 q
+= snprintf(q
, c
->buffer_size
,
1424 "HTTP/1.0 200 Server too busy\r\n"
1425 "Content-type: text/html\r\n"
1427 "<html><head><title>Too busy</title></head><body>\r\n"
1428 "<p>The server is too busy to serve your request at this time.</p>\r\n"
1429 "<p>The bandwidth being served (including your stream) is %"PRIu64
"kbit/sec, "
1430 "and this exceeds the limit of %"PRIu64
"kbit/sec.</p>\r\n"
1431 "</body></html>\r\n", current_bandwidth
, max_bandwidth
);
1432 /* prepare output buffer */
1433 c
->buffer_ptr
= c
->buffer
;
1435 c
->state
= HTTPSTATE_SEND_HEADER
;
1439 if (redir_type
!= REDIR_NONE
) {
1442 for (p
= c
->buffer
; *p
&& *p
!= '\r' && *p
!= '\n'; ) {
1443 if (strncasecmp(p
, "Host:", 5) == 0) {
1447 p
= strchr(p
, '\n');
1458 while (isspace(*hostinfo
))
1461 eoh
= strchr(hostinfo
, '\n');
1463 if (eoh
[-1] == '\r')
1466 if (eoh
- hostinfo
< sizeof(hostbuf
) - 1) {
1467 memcpy(hostbuf
, hostinfo
, eoh
- hostinfo
);
1468 hostbuf
[eoh
- hostinfo
] = 0;
1470 c
->http_error
= 200;
1472 switch(redir_type
) {
1474 q
+= snprintf(q
, c
->buffer_size
,
1475 "HTTP/1.0 200 ASX Follows\r\n"
1476 "Content-type: video/x-ms-asf\r\n"
1478 "<ASX Version=\"3\">\r\n"
1479 //"<!-- Autogenerated by ffserver -->\r\n"
1480 "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n"
1481 "</ASX>\r\n", hostbuf
, filename
, info
);
1484 q
+= snprintf(q
, c
->buffer_size
,
1485 "HTTP/1.0 200 RAM Follows\r\n"
1486 "Content-type: audio/x-pn-realaudio\r\n"
1488 "# Autogenerated by ffserver\r\n"
1489 "http://%s/%s%s\r\n", hostbuf
, filename
, info
);
1492 q
+= snprintf(q
, c
->buffer_size
,
1493 "HTTP/1.0 200 ASF Redirect follows\r\n"
1494 "Content-type: video/x-ms-asf\r\n"
1497 "Ref1=http://%s/%s%s\r\n", hostbuf
, filename
, info
);
1501 char hostname
[256], *p
;
1502 /* extract only hostname */
1503 av_strlcpy(hostname
, hostbuf
, sizeof(hostname
));
1504 p
= strrchr(hostname
, ':');
1507 q
+= snprintf(q
, c
->buffer_size
,
1508 "HTTP/1.0 200 RTSP Redirect follows\r\n"
1509 /* XXX: incorrect mime type ? */
1510 "Content-type: application/x-rtsp\r\n"
1512 "rtsp://%s:%d/%s\r\n", hostname
, ntohs(my_rtsp_addr
.sin_port
), filename
);
1518 int sdp_data_size
, len
;
1519 struct sockaddr_in my_addr
;
1521 q
+= snprintf(q
, c
->buffer_size
,
1522 "HTTP/1.0 200 OK\r\n"
1523 "Content-type: application/sdp\r\n"
1526 len
= sizeof(my_addr
);
1527 getsockname(c
->fd
, (struct sockaddr
*)&my_addr
, &len
);
1529 /* XXX: should use a dynamic buffer */
1530 sdp_data_size
= prepare_sdp_description(stream
,
1533 if (sdp_data_size
> 0) {
1534 memcpy(q
, sdp_data
, sdp_data_size
);
1546 /* prepare output buffer */
1547 c
->buffer_ptr
= c
->buffer
;
1549 c
->state
= HTTPSTATE_SEND_HEADER
;
1555 snprintf(msg
, sizeof(msg
), "ASX/RAM file not handled");
1559 stream
->conns_served
++;
1561 /* XXX: add there authenticate and IP match */
1564 /* if post, it means a feed is being sent */
1565 if (!stream
->is_feed
) {
1566 /* However it might be a status report from WMP! Let us log the
1567 * data as it might come in handy one day. */
1571 for (p
= c
->buffer
; *p
&& *p
!= '\r' && *p
!= '\n'; ) {
1572 if (strncasecmp(p
, "Pragma: log-line=", 17) == 0) {
1576 if (strncasecmp(p
, "Pragma: client-id=", 18) == 0)
1577 client_id
= strtol(p
+ 18, 0, 10);
1578 p
= strchr(p
, '\n');
1586 char *eol
= strchr(logline
, '\n');
1591 if (eol
[-1] == '\r')
1593 http_log("%.*s\n", (int) (eol
- logline
), logline
);
1594 c
->suppress_log
= 1;
1599 http_log("\nGot request:\n%s\n", c
->buffer
);
1602 if (client_id
&& extract_rates(ratebuf
, sizeof(ratebuf
), c
->buffer
)) {
1605 /* Now we have to find the client_id */
1606 for (wmpc
= first_http_ctx
; wmpc
; wmpc
= wmpc
->next
) {
1607 if (wmpc
->wmp_client_id
== client_id
)
1611 if (wmpc
&& modify_current_stream(wmpc
, ratebuf
))
1612 wmpc
->switch_pending
= 1;
1615 snprintf(msg
, sizeof(msg
), "POST command not handled");
1619 if (http_start_receive_data(c
) < 0) {
1620 snprintf(msg
, sizeof(msg
), "could not open feed");
1624 c
->state
= HTTPSTATE_RECEIVE_DATA
;
1629 if (strcmp(stream
->filename
+ strlen(stream
->filename
) - 4, ".asf") == 0)
1630 http_log("\nGot request:\n%s\n", c
->buffer
);
1633 if (c
->stream
->stream_type
== STREAM_TYPE_STATUS
)
1636 /* open input stream */
1637 if (open_input_stream(c
, info
) < 0) {
1638 snprintf(msg
, sizeof(msg
), "Input stream corresponding to '%s' not found", url
);
1642 /* prepare http header */
1644 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "HTTP/1.0 200 OK\r\n");
1645 mime_type
= c
->stream
->fmt
->mime_type
;
1647 mime_type
= "application/x-octet-stream";
1648 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "Pragma: no-cache\r\n");
1650 /* for asf, we need extra headers */
1651 if (!strcmp(c
->stream
->fmt
->name
,"asf_stream")) {
1652 /* Need to allocate a client id */
1654 c
->wmp_client_id
= av_lfg_get(&random_state
);
1656 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
);
1658 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "Content-Type: %s\r\n", mime_type
);
1659 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "\r\n");
1661 /* prepare output buffer */
1663 c
->buffer_ptr
= c
->buffer
;
1665 c
->state
= HTTPSTATE_SEND_HEADER
;
1668 c
->http_error
= 404;
1670 q
+= snprintf(q
, c
->buffer_size
,
1671 "HTTP/1.0 404 Not Found\r\n"
1672 "Content-type: text/html\r\n"
1675 "<head><title>404 Not Found</title></head>\n"
1678 /* prepare output buffer */
1679 c
->buffer_ptr
= c
->buffer
;
1681 c
->state
= HTTPSTATE_SEND_HEADER
;
1685 c
->http_error
= 200; /* horrible : we use this value to avoid
1686 going to the send data state */
1687 c
->state
= HTTPSTATE_SEND_HEADER
;
1691 static void fmt_bytecount(ByteIOContext
*pb
, int64_t count
)
1693 static const char *suffix
= " kMGTP";
1696 for (s
= suffix
; count
>= 100000 && s
[1]; count
/= 1000, s
++);
1698 url_fprintf(pb
, "%"PRId64
"%c", count
, *s
);
1701 static void compute_status(HTTPContext
*c
)
1710 if (url_open_dyn_buf(&pb
) < 0) {
1711 /* XXX: return an error ? */
1712 c
->buffer_ptr
= c
->buffer
;
1713 c
->buffer_end
= c
->buffer
;
1717 url_fprintf(pb
, "HTTP/1.0 200 OK\r\n");
1718 url_fprintf(pb
, "Content-type: %s\r\n", "text/html");
1719 url_fprintf(pb
, "Pragma: no-cache\r\n");
1720 url_fprintf(pb
, "\r\n");
1722 url_fprintf(pb
, "<html><head><title>%s Status</title>\n", program_name
);
1723 if (c
->stream
->feed_filename
[0])
1724 url_fprintf(pb
, "<link rel=\"shortcut icon\" href=\"%s\">\n", c
->stream
->feed_filename
);
1725 url_fprintf(pb
, "</head>\n<body>");
1726 url_fprintf(pb
, "<h1>%s Status</h1>\n", program_name
);
1728 url_fprintf(pb
, "<h2>Available Streams</h2>\n");
1729 url_fprintf(pb
, "<table cellspacing=0 cellpadding=4>\n");
1730 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");
1731 stream
= first_stream
;
1732 while (stream
!= NULL
) {
1733 char sfilename
[1024];
1736 if (stream
->feed
!= stream
) {
1737 av_strlcpy(sfilename
, stream
->filename
, sizeof(sfilename
) - 10);
1738 eosf
= sfilename
+ strlen(sfilename
);
1739 if (eosf
- sfilename
>= 4) {
1740 if (strcmp(eosf
- 4, ".asf") == 0)
1741 strcpy(eosf
- 4, ".asx");
1742 else if (strcmp(eosf
- 3, ".rm") == 0)
1743 strcpy(eosf
- 3, ".ram");
1744 else if (stream
->fmt
&& !strcmp(stream
->fmt
->name
, "rtp")) {
1745 /* generate a sample RTSP director if
1746 unicast. Generate an SDP redirector if
1748 eosf
= strrchr(sfilename
, '.');
1750 eosf
= sfilename
+ strlen(sfilename
);
1751 if (stream
->is_multicast
)
1752 strcpy(eosf
, ".sdp");
1754 strcpy(eosf
, ".rtsp");
1758 url_fprintf(pb
, "<tr><td><a href=\"/%s\">%s</a> ",
1759 sfilename
, stream
->filename
);
1760 url_fprintf(pb
, "<td align=right> %d <td align=right> ",
1761 stream
->conns_served
);
1762 fmt_bytecount(pb
, stream
->bytes_served
);
1763 switch(stream
->stream_type
) {
1764 case STREAM_TYPE_LIVE
: {
1765 int audio_bit_rate
= 0;
1766 int video_bit_rate
= 0;
1767 const char *audio_codec_name
= "";
1768 const char *video_codec_name
= "";
1769 const char *audio_codec_name_extra
= "";
1770 const char *video_codec_name_extra
= "";
1772 for(i
=0;i
<stream
->nb_streams
;i
++) {
1773 AVStream
*st
= stream
->streams
[i
];
1774 AVCodec
*codec
= avcodec_find_encoder(st
->codec
->codec_id
);
1775 switch(st
->codec
->codec_type
) {
1776 case CODEC_TYPE_AUDIO
:
1777 audio_bit_rate
+= st
->codec
->bit_rate
;
1779 if (*audio_codec_name
)
1780 audio_codec_name_extra
= "...";
1781 audio_codec_name
= codec
->name
;
1784 case CODEC_TYPE_VIDEO
:
1785 video_bit_rate
+= st
->codec
->bit_rate
;
1787 if (*video_codec_name
)
1788 video_codec_name_extra
= "...";
1789 video_codec_name
= codec
->name
;
1792 case CODEC_TYPE_DATA
:
1793 video_bit_rate
+= st
->codec
->bit_rate
;
1799 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",
1802 video_bit_rate
/ 1000, video_codec_name
, video_codec_name_extra
,
1803 audio_bit_rate
/ 1000, audio_codec_name
, audio_codec_name_extra
);
1805 url_fprintf(pb
, "<td>%s", stream
->feed
->filename
);
1807 url_fprintf(pb
, "<td>%s", stream
->feed_filename
);
1808 url_fprintf(pb
, "\n");
1812 url_fprintf(pb
, "<td align=center> - <td align=right> - <td align=right> - <td><td align=right> - <td>\n");
1816 stream
= stream
->next
;
1818 url_fprintf(pb
, "</table>\n");
1820 stream
= first_stream
;
1821 while (stream
!= NULL
) {
1822 if (stream
->feed
== stream
) {
1823 url_fprintf(pb
, "<h2>Feed %s</h2>", stream
->filename
);
1825 url_fprintf(pb
, "Running as pid %d.\n", stream
->pid
);
1827 #if defined(linux) && !defined(CONFIG_NOCUTILS)
1832 /* This is somewhat linux specific I guess */
1833 snprintf(ps_cmd
, sizeof(ps_cmd
),
1834 "ps -o \"%%cpu,cputime\" --no-headers %d",
1837 pid_stat
= popen(ps_cmd
, "r");
1842 if (fscanf(pid_stat
, "%10s %64s", cpuperc
,
1844 url_fprintf(pb
, "Currently using %s%% of the cpu. Total time used %s.\n",
1852 url_fprintf(pb
, "<p>");
1854 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");
1856 for (i
= 0; i
< stream
->nb_streams
; i
++) {
1857 AVStream
*st
= stream
->streams
[i
];
1858 AVCodec
*codec
= avcodec_find_encoder(st
->codec
->codec_id
);
1859 const char *type
= "unknown";
1860 char parameters
[64];
1864 switch(st
->codec
->codec_type
) {
1865 case CODEC_TYPE_AUDIO
:
1867 snprintf(parameters
, sizeof(parameters
), "%d channel(s), %d Hz", st
->codec
->channels
, st
->codec
->sample_rate
);
1869 case CODEC_TYPE_VIDEO
:
1871 snprintf(parameters
, sizeof(parameters
), "%dx%d, q=%d-%d, fps=%d", st
->codec
->width
, st
->codec
->height
,
1872 st
->codec
->qmin
, st
->codec
->qmax
, st
->codec
->time_base
.den
/ st
->codec
->time_base
.num
);
1877 url_fprintf(pb
, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
1878 i
, type
, st
->codec
->bit_rate
/1000, codec ? codec
->name
: "", parameters
);
1880 url_fprintf(pb
, "</table>\n");
1883 stream
= stream
->next
;
1886 /* connection status */
1887 url_fprintf(pb
, "<h2>Connection Status</h2>\n");
1889 url_fprintf(pb
, "Number of connections: %d / %d<br>\n",
1890 nb_connections
, nb_max_connections
);
1892 url_fprintf(pb
, "Bandwidth in use: %"PRIu64
"k / %"PRIu64
"k<br>\n",
1893 current_bandwidth
, max_bandwidth
);
1895 url_fprintf(pb
, "<table>\n");
1896 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");
1897 c1
= first_http_ctx
;
1899 while (c1
!= NULL
) {
1905 for (j
= 0; j
< c1
->stream
->nb_streams
; j
++) {
1906 if (!c1
->stream
->feed
)
1907 bitrate
+= c1
->stream
->streams
[j
]->codec
->bit_rate
;
1908 else if (c1
->feed_streams
[j
] >= 0)
1909 bitrate
+= c1
->stream
->feed
->streams
[c1
->feed_streams
[j
]]->codec
->bit_rate
;
1914 p
= inet_ntoa(c1
->from_addr
.sin_addr
);
1915 url_fprintf(pb
, "<tr><td><b>%d</b><td>%s%s<td>%s<td>%s<td>%s<td align=right>",
1917 c1
->stream ? c1
->stream
->filename
: "",
1918 c1
->state
== HTTPSTATE_RECEIVE_DATA ?
"(input)" : "",
1921 http_state
[c1
->state
]);
1922 fmt_bytecount(pb
, bitrate
);
1923 url_fprintf(pb
, "<td align=right>");
1924 fmt_bytecount(pb
, compute_datarate(&c1
->datarate
, c1
->data_count
) * 8);
1925 url_fprintf(pb
, "<td align=right>");
1926 fmt_bytecount(pb
, c1
->data_count
);
1927 url_fprintf(pb
, "\n");
1930 url_fprintf(pb
, "</table>\n");
1935 url_fprintf(pb
, "<hr size=1 noshade>Generated at %s", p
);
1936 url_fprintf(pb
, "</body>\n</html>\n");
1938 len
= url_close_dyn_buf(pb
, &c
->pb_buffer
);
1939 c
->buffer_ptr
= c
->pb_buffer
;
1940 c
->buffer_end
= c
->pb_buffer
+ len
;
1943 /* check if the parser needs to be opened for stream i */
1944 static void open_parser(AVFormatContext
*s
, int i
)
1946 AVStream
*st
= s
->streams
[i
];
1949 if (!st
->codec
->codec
) {
1950 codec
= avcodec_find_decoder(st
->codec
->codec_id
);
1951 if (codec
&& (codec
->capabilities
& CODEC_CAP_PARSE_ONLY
)) {
1952 st
->codec
->parse_only
= 1;
1953 if (avcodec_open(st
->codec
, codec
) < 0)
1954 st
->codec
->parse_only
= 0;
1959 static int open_input_stream(HTTPContext
*c
, const char *info
)
1962 char input_filename
[1024];
1964 int buf_size
, i
, ret
;
1967 /* find file name */
1968 if (c
->stream
->feed
) {
1969 strcpy(input_filename
, c
->stream
->feed
->feed_filename
);
1970 buf_size
= FFM_PACKET_SIZE
;
1971 /* compute position (absolute time) */
1972 if (find_info_tag(buf
, sizeof(buf
), "date", info
)) {
1973 stream_pos
= parse_date(buf
, 0);
1974 if (stream_pos
== INT64_MIN
)
1976 } else if (find_info_tag(buf
, sizeof(buf
), "buffer", info
)) {
1977 int prebuffer
= strtol(buf
, 0, 10);
1978 stream_pos
= av_gettime() - prebuffer
* (int64_t)1000000;
1980 stream_pos
= av_gettime() - c
->stream
->prebuffer
* (int64_t)1000;
1982 strcpy(input_filename
, c
->stream
->feed_filename
);
1984 /* compute position (relative time) */
1985 if (find_info_tag(buf
, sizeof(buf
), "date", info
)) {
1986 stream_pos
= parse_date(buf
, 1);
1987 if (stream_pos
== INT64_MIN
)
1992 if (input_filename
[0] == '\0')
1996 if ((ret
= av_open_input_file(&s
, input_filename
, c
->stream
->ifmt
,
1997 buf_size
, c
->stream
->ap_in
)) < 0) {
1998 http_log("could not open %s: %d\n", input_filename
, ret
);
2001 s
->flags
|= AVFMT_FLAG_GENPTS
;
2003 if (strcmp(s
->iformat
->name
, "ffm") && av_find_stream_info(c
->fmt_in
) < 0) {
2004 http_log("Could not find stream info '%s'\n", input_filename
);
2005 av_close_input_file(s
);
2009 /* open each parser */
2010 for(i
=0;i
<s
->nb_streams
;i
++)
2013 /* choose stream as clock source (we favorize video stream if
2014 present) for packet sending */
2015 c
->pts_stream_index
= 0;
2016 for(i
=0;i
<c
->stream
->nb_streams
;i
++) {
2017 if (c
->pts_stream_index
== 0 &&
2018 c
->stream
->streams
[i
]->codec
->codec_type
== CODEC_TYPE_VIDEO
) {
2019 c
->pts_stream_index
= i
;
2024 if (c
->fmt_in
->iformat
->read_seek
)
2025 av_seek_frame(c
->fmt_in
, -1, stream_pos
, 0);
2027 /* set the start time (needed for maxtime and RTP packet timing) */
2028 c
->start_time
= cur_time
;
2029 c
->first_pts
= AV_NOPTS_VALUE
;
2033 /* return the server clock (in us) */
2034 static int64_t get_server_clock(HTTPContext
*c
)
2036 /* compute current pts value from system time */
2037 return (cur_time
- c
->start_time
) * 1000;
2040 /* return the estimated time at which the current packet must be sent
2042 static int64_t get_packet_send_clock(HTTPContext
*c
)
2044 int bytes_left
, bytes_sent
, frame_bytes
;
2046 frame_bytes
= c
->cur_frame_bytes
;
2047 if (frame_bytes
<= 0)
2050 bytes_left
= c
->buffer_end
- c
->buffer_ptr
;
2051 bytes_sent
= frame_bytes
- bytes_left
;
2052 return c
->cur_pts
+ (c
->cur_frame_duration
* bytes_sent
) / frame_bytes
;
2057 static int http_prepare_data(HTTPContext
*c
)
2060 AVFormatContext
*ctx
;
2062 av_freep(&c
->pb_buffer
);
2064 case HTTPSTATE_SEND_DATA_HEADER
:
2065 memset(&c
->fmt_ctx
, 0, sizeof(c
->fmt_ctx
));
2066 av_metadata_set(&c
->fmt_ctx
.metadata
, "author" ,c
->stream
->author
);
2067 av_metadata_set(&c
->fmt_ctx
.metadata
, "comment" ,c
->stream
->comment
);
2068 av_metadata_set(&c
->fmt_ctx
.metadata
, "copyright",c
->stream
->copyright
);
2069 av_metadata_set(&c
->fmt_ctx
.metadata
, "title" ,c
->stream
->title
);
2071 for(i
=0;i
<c
->stream
->nb_streams
;i
++) {
2074 st
= av_mallocz(sizeof(AVStream
));
2075 c
->fmt_ctx
.streams
[i
] = st
;
2076 /* if file or feed, then just take streams from FFStream struct */
2077 if (!c
->stream
->feed
||
2078 c
->stream
->feed
== c
->stream
)
2079 src
= c
->stream
->streams
[i
];
2081 src
= c
->stream
->feed
->streams
[c
->stream
->feed_streams
[i
]];
2085 st
->codec
->frame_number
= 0; /* XXX: should be done in
2086 AVStream, not in codec */
2088 /* set output format parameters */
2089 c
->fmt_ctx
.oformat
= c
->stream
->fmt
;
2090 c
->fmt_ctx
.nb_streams
= c
->stream
->nb_streams
;
2092 c
->got_key_frame
= 0;
2094 /* prepare header and save header data in a stream */
2095 if (url_open_dyn_buf(&c
->fmt_ctx
.pb
) < 0) {
2096 /* XXX: potential leak */
2099 c
->fmt_ctx
.pb
->is_streamed
= 1;
2102 * HACK to avoid mpeg ps muxer to spit many underflow errors
2103 * Default value from FFmpeg
2104 * Try to set it use configuration option
2106 c
->fmt_ctx
.preload
= (int)(0.5*AV_TIME_BASE
);
2107 c
->fmt_ctx
.max_delay
= (int)(0.7*AV_TIME_BASE
);
2109 av_set_parameters(&c
->fmt_ctx
, NULL
);
2110 if (av_write_header(&c
->fmt_ctx
) < 0) {
2111 http_log("Error writing output header\n");
2115 len
= url_close_dyn_buf(c
->fmt_ctx
.pb
, &c
->pb_buffer
);
2116 c
->buffer_ptr
= c
->pb_buffer
;
2117 c
->buffer_end
= c
->pb_buffer
+ len
;
2119 c
->state
= HTTPSTATE_SEND_DATA
;
2120 c
->last_packet_sent
= 0;
2122 case HTTPSTATE_SEND_DATA
:
2123 /* find a new packet */
2124 /* read a packet from the input stream */
2125 if (c
->stream
->feed
)
2126 ffm_set_write_index(c
->fmt_in
,
2127 c
->stream
->feed
->feed_write_index
,
2128 c
->stream
->feed
->feed_size
);
2130 if (c
->stream
->max_time
&&
2131 c
->stream
->max_time
+ c
->start_time
- cur_time
< 0)
2132 /* We have timed out */
2133 c
->state
= HTTPSTATE_SEND_DATA_TRAILER
;
2137 if (av_read_frame(c
->fmt_in
, &pkt
) < 0) {
2138 if (c
->stream
->feed
&& c
->stream
->feed
->feed_opened
) {
2139 /* if coming from feed, it means we reached the end of the
2140 ffm file, so must wait for more data */
2141 c
->state
= HTTPSTATE_WAIT_FEED
;
2142 return 1; /* state changed */
2144 if (c
->stream
->loop
) {
2145 av_close_input_file(c
->fmt_in
);
2147 if (open_input_stream(c
, "") < 0)
2152 /* must send trailer now because eof or error */
2153 c
->state
= HTTPSTATE_SEND_DATA_TRAILER
;
2157 int source_index
= pkt
.stream_index
;
2158 /* update first pts if needed */
2159 if (c
->first_pts
== AV_NOPTS_VALUE
) {
2160 c
->first_pts
= av_rescale_q(pkt
.dts
, c
->fmt_in
->streams
[pkt
.stream_index
]->time_base
, AV_TIME_BASE_Q
);
2161 c
->start_time
= cur_time
;
2163 /* send it to the appropriate stream */
2164 if (c
->stream
->feed
) {
2165 /* if coming from a feed, select the right stream */
2166 if (c
->switch_pending
) {
2167 c
->switch_pending
= 0;
2168 for(i
=0;i
<c
->stream
->nb_streams
;i
++) {
2169 if (c
->switch_feed_streams
[i
] == pkt
.stream_index
)
2170 if (pkt
.flags
& PKT_FLAG_KEY
)
2171 do_switch_stream(c
, i
);
2172 if (c
->switch_feed_streams
[i
] >= 0)
2173 c
->switch_pending
= 1;
2176 for(i
=0;i
<c
->stream
->nb_streams
;i
++) {
2177 if (c
->feed_streams
[i
] == pkt
.stream_index
) {
2178 AVStream
*st
= c
->fmt_in
->streams
[source_index
];
2179 pkt
.stream_index
= i
;
2180 if (pkt
.flags
& PKT_FLAG_KEY
&&
2181 (st
->codec
->codec_type
== CODEC_TYPE_VIDEO
||
2182 c
->stream
->nb_streams
== 1))
2183 c
->got_key_frame
= 1;
2184 if (!c
->stream
->send_on_key
|| c
->got_key_frame
)
2189 AVCodecContext
*codec
;
2190 AVStream
*ist
, *ost
;
2192 ist
= c
->fmt_in
->streams
[source_index
];
2193 /* specific handling for RTP: we use several
2194 output stream (one for each RTP
2195 connection). XXX: need more abstract handling */
2196 if (c
->is_packetized
) {
2197 /* compute send time and duration */
2198 c
->cur_pts
= av_rescale_q(pkt
.dts
, ist
->time_base
, AV_TIME_BASE_Q
);
2199 if (ist
->start_time
!= AV_NOPTS_VALUE
)
2200 c
->cur_pts
-= av_rescale_q(ist
->start_time
, ist
->time_base
, AV_TIME_BASE_Q
);
2201 c
->cur_frame_duration
= av_rescale_q(pkt
.duration
, ist
->time_base
, AV_TIME_BASE_Q
);
2202 /* find RTP context */
2203 c
->packet_stream_index
= pkt
.stream_index
;
2204 ctx
= c
->rtp_ctx
[c
->packet_stream_index
];
2206 av_free_packet(&pkt
);
2209 codec
= ctx
->streams
[0]->codec
;
2210 /* only one stream per RTP connection */
2211 pkt
.stream_index
= 0;
2215 codec
= ctx
->streams
[pkt
.stream_index
]->codec
;
2218 if (c
->is_packetized
) {
2219 int max_packet_size
;
2220 if (c
->rtp_protocol
== RTSP_LOWER_TRANSPORT_TCP
)
2221 max_packet_size
= RTSP_TCP_MAX_PACKET_SIZE
;
2223 max_packet_size
= url_get_max_packet_size(c
->rtp_handles
[c
->packet_stream_index
]);
2224 ret
= url_open_dyn_packet_buf(&ctx
->pb
, max_packet_size
);
2226 ret
= url_open_dyn_buf(&ctx
->pb
);
2229 /* XXX: potential leak */
2232 ost
= ctx
->streams
[pkt
.stream_index
];
2234 ctx
->pb
->is_streamed
= 1;
2235 if (pkt
.dts
!= AV_NOPTS_VALUE
)
2236 pkt
.dts
= av_rescale_q(pkt
.dts
, ist
->time_base
, ost
->time_base
);
2237 if (pkt
.pts
!= AV_NOPTS_VALUE
)
2238 pkt
.pts
= av_rescale_q(pkt
.pts
, ist
->time_base
, ost
->time_base
);
2239 pkt
.duration
= av_rescale_q(pkt
.duration
, ist
->time_base
, ost
->time_base
);
2240 if (av_write_frame(ctx
, &pkt
) < 0) {
2241 http_log("Error writing frame to output\n");
2242 c
->state
= HTTPSTATE_SEND_DATA_TRAILER
;
2245 len
= url_close_dyn_buf(ctx
->pb
, &c
->pb_buffer
);
2246 c
->cur_frame_bytes
= len
;
2247 c
->buffer_ptr
= c
->pb_buffer
;
2248 c
->buffer_end
= c
->pb_buffer
+ len
;
2250 codec
->frame_number
++;
2252 av_free_packet(&pkt
);
2256 av_free_packet(&pkt
);
2261 case HTTPSTATE_SEND_DATA_TRAILER
:
2262 /* last packet test ? */
2263 if (c
->last_packet_sent
|| c
->is_packetized
)
2266 /* prepare header */
2267 if (url_open_dyn_buf(&ctx
->pb
) < 0) {
2268 /* XXX: potential leak */
2271 c
->fmt_ctx
.pb
->is_streamed
= 1;
2272 av_write_trailer(ctx
);
2273 len
= url_close_dyn_buf(ctx
->pb
, &c
->pb_buffer
);
2274 c
->buffer_ptr
= c
->pb_buffer
;
2275 c
->buffer_end
= c
->pb_buffer
+ len
;
2277 c
->last_packet_sent
= 1;
2283 /* should convert the format at the same time */
2284 /* send data starting at c->buffer_ptr to the output connection
2285 (either UDP or TCP connection) */
2286 static int http_send_data(HTTPContext
*c
)
2291 if (c
->buffer_ptr
>= c
->buffer_end
) {
2292 ret
= http_prepare_data(c
);
2296 /* state change requested */
2299 if (c
->is_packetized
) {
2300 /* RTP data output */
2301 len
= c
->buffer_end
- c
->buffer_ptr
;
2303 /* fail safe - should never happen */
2305 c
->buffer_ptr
= c
->buffer_end
;
2308 len
= (c
->buffer_ptr
[0] << 24) |
2309 (c
->buffer_ptr
[1] << 16) |
2310 (c
->buffer_ptr
[2] << 8) |
2312 if (len
> (c
->buffer_end
- c
->buffer_ptr
))
2314 if ((get_packet_send_clock(c
) - get_server_clock(c
)) > 0) {
2315 /* nothing to send yet: we can wait */
2319 c
->data_count
+= len
;
2320 update_datarate(&c
->datarate
, c
->data_count
);
2322 c
->stream
->bytes_served
+= len
;
2324 if (c
->rtp_protocol
== RTSP_LOWER_TRANSPORT_TCP
) {
2325 /* RTP packets are sent inside the RTSP TCP connection */
2327 int interleaved_index
, size
;
2329 HTTPContext
*rtsp_c
;
2332 /* if no RTSP connection left, error */
2335 /* if already sending something, then wait. */
2336 if (rtsp_c
->state
!= RTSPSTATE_WAIT_REQUEST
)
2338 if (url_open_dyn_buf(&pb
) < 0)
2340 interleaved_index
= c
->packet_stream_index
* 2;
2341 /* RTCP packets are sent at odd indexes */
2342 if (c
->buffer_ptr
[1] == 200)
2343 interleaved_index
++;
2344 /* write RTSP TCP header */
2346 header
[1] = interleaved_index
;
2347 header
[2] = len
>> 8;
2349 put_buffer(pb
, header
, 4);
2350 /* write RTP packet data */
2352 put_buffer(pb
, c
->buffer_ptr
, len
);
2353 size
= url_close_dyn_buf(pb
, &c
->packet_buffer
);
2354 /* prepare asynchronous TCP sending */
2355 rtsp_c
->packet_buffer_ptr
= c
->packet_buffer
;
2356 rtsp_c
->packet_buffer_end
= c
->packet_buffer
+ size
;
2357 c
->buffer_ptr
+= len
;
2359 /* send everything we can NOW */
2360 len
= send(rtsp_c
->fd
, rtsp_c
->packet_buffer_ptr
,
2361 rtsp_c
->packet_buffer_end
- rtsp_c
->packet_buffer_ptr
, 0);
2363 rtsp_c
->packet_buffer_ptr
+= len
;
2364 if (rtsp_c
->packet_buffer_ptr
< rtsp_c
->packet_buffer_end
) {
2365 /* if we could not send all the data, we will
2366 send it later, so a new state is needed to
2367 "lock" the RTSP TCP connection */
2368 rtsp_c
->state
= RTSPSTATE_SEND_PACKET
;
2371 /* all data has been sent */
2372 av_freep(&c
->packet_buffer
);
2374 /* send RTP packet directly in UDP */
2376 url_write(c
->rtp_handles
[c
->packet_stream_index
],
2377 c
->buffer_ptr
, len
);
2378 c
->buffer_ptr
+= len
;
2379 /* here we continue as we can send several packets per 10 ms slot */
2382 /* TCP data output */
2383 len
= send(c
->fd
, c
->buffer_ptr
, c
->buffer_end
- c
->buffer_ptr
, 0);
2385 if (ff_neterrno() != FF_NETERROR(EAGAIN
) &&
2386 ff_neterrno() != FF_NETERROR(EINTR
))
2387 /* error : close connection */
2392 c
->buffer_ptr
+= len
;
2394 c
->data_count
+= len
;
2395 update_datarate(&c
->datarate
, c
->data_count
);
2397 c
->stream
->bytes_served
+= len
;
2405 static int http_start_receive_data(HTTPContext
*c
)
2409 if (c
->stream
->feed_opened
)
2412 /* Don't permit writing to this one */
2413 if (c
->stream
->readonly
)
2417 fd
= open(c
->stream
->feed_filename
, O_RDWR
);
2419 http_log("Error opening feeder file: %s\n", strerror(errno
));
2424 if (c
->stream
->truncate
) {
2425 /* truncate feed file */
2426 ffm_write_write_index(c
->feed_fd
, FFM_PACKET_SIZE
);
2427 ftruncate(c
->feed_fd
, FFM_PACKET_SIZE
);
2428 http_log("Truncating feed file '%s'\n", c
->stream
->feed_filename
);
2430 if ((c
->stream
->feed_write_index
= ffm_read_write_index(fd
)) < 0) {
2431 http_log("Error reading write index from feed file: %s\n", strerror(errno
));
2436 c
->stream
->feed_write_index
= FFMAX(ffm_read_write_index(fd
), FFM_PACKET_SIZE
);
2437 c
->stream
->feed_size
= lseek(fd
, 0, SEEK_END
);
2438 lseek(fd
, 0, SEEK_SET
);
2440 /* init buffer input */
2441 c
->buffer_ptr
= c
->buffer
;
2442 c
->buffer_end
= c
->buffer
+ FFM_PACKET_SIZE
;
2443 c
->stream
->feed_opened
= 1;
2444 c
->chunked_encoding
= !!av_stristr(c
->buffer
, "Transfer-Encoding: chunked");
2448 static int http_receive_data(HTTPContext
*c
)
2451 int len
, loop_run
= 0;
2453 while (c
->chunked_encoding
&& !c
->chunk_size
&&
2454 c
->buffer_end
> c
->buffer_ptr
) {
2455 /* read chunk header, if present */
2456 len
= recv(c
->fd
, c
->buffer_ptr
, 1, 0);
2459 if (ff_neterrno() != FF_NETERROR(EAGAIN
) &&
2460 ff_neterrno() != FF_NETERROR(EINTR
))
2461 /* error : close connection */
2463 } else if (len
== 0) {
2464 /* end of connection : close it */
2466 } else if (c
->buffer_ptr
- c
->buffer
>= 2 &&
2467 !memcmp(c
->buffer_ptr
- 1, "\r\n", 2)) {
2468 c
->chunk_size
= strtol(c
->buffer
, 0, 16);
2469 if (c
->chunk_size
== 0) // end of stream
2471 c
->buffer_ptr
= c
->buffer
;
2473 } else if (++loop_run
> 10) {
2474 /* no chunk header, abort */
2481 if (c
->buffer_end
> c
->buffer_ptr
) {
2482 len
= recv(c
->fd
, c
->buffer_ptr
,
2483 FFMIN(c
->chunk_size
, c
->buffer_end
- c
->buffer_ptr
), 0);
2485 if (ff_neterrno() != FF_NETERROR(EAGAIN
) &&
2486 ff_neterrno() != FF_NETERROR(EINTR
))
2487 /* error : close connection */
2489 } else if (len
== 0)
2490 /* end of connection : close it */
2493 c
->chunk_size
-= len
;
2494 c
->buffer_ptr
+= len
;
2495 c
->data_count
+= len
;
2496 update_datarate(&c
->datarate
, c
->data_count
);
2500 if (c
->buffer_ptr
- c
->buffer
>= 2 && c
->data_count
> FFM_PACKET_SIZE
) {
2501 if (c
->buffer
[0] != 'f' ||
2502 c
->buffer
[1] != 'm') {
2503 http_log("Feed stream has become desynchronized -- disconnecting\n");
2508 if (c
->buffer_ptr
>= c
->buffer_end
) {
2509 FFStream
*feed
= c
->stream
;
2510 /* a packet has been received : write it in the store, except
2512 if (c
->data_count
> FFM_PACKET_SIZE
) {
2514 // printf("writing pos=0x%"PRIx64" size=0x%"PRIx64"\n", feed->feed_write_index, feed->feed_size);
2515 /* XXX: use llseek or url_seek */
2516 lseek(c
->feed_fd
, feed
->feed_write_index
, SEEK_SET
);
2517 if (write(c
->feed_fd
, c
->buffer
, FFM_PACKET_SIZE
) < 0) {
2518 http_log("Error writing to feed file: %s\n", strerror(errno
));
2522 feed
->feed_write_index
+= FFM_PACKET_SIZE
;
2523 /* update file size */
2524 if (feed
->feed_write_index
> c
->stream
->feed_size
)
2525 feed
->feed_size
= feed
->feed_write_index
;
2527 /* handle wrap around if max file size reached */
2528 if (c
->stream
->feed_max_size
&& feed
->feed_write_index
>= c
->stream
->feed_max_size
)
2529 feed
->feed_write_index
= FFM_PACKET_SIZE
;
2532 if (ffm_write_write_index(c
->feed_fd
, feed
->feed_write_index
) < 0) {
2533 http_log("Error writing index to feed file: %s\n", strerror(errno
));
2537 /* wake up any waiting connections */
2538 for(c1
= first_http_ctx
; c1
!= NULL
; c1
= c1
->next
) {
2539 if (c1
->state
== HTTPSTATE_WAIT_FEED
&&
2540 c1
->stream
->feed
== c
->stream
->feed
)
2541 c1
->state
= HTTPSTATE_SEND_DATA
;
2544 /* We have a header in our hands that contains useful data */
2545 AVFormatContext
*s
= NULL
;
2547 AVInputFormat
*fmt_in
;
2550 /* use feed output format name to find corresponding input format */
2551 fmt_in
= av_find_input_format(feed
->fmt
->name
);
2555 url_open_buf(&pb
, c
->buffer
, c
->buffer_end
- c
->buffer
, URL_RDONLY
);
2556 pb
->is_streamed
= 1;
2558 if (av_open_input_stream(&s
, pb
, c
->stream
->feed_filename
, fmt_in
, NULL
) < 0) {
2563 /* Now we have the actual streams */
2564 if (s
->nb_streams
!= feed
->nb_streams
) {
2565 av_close_input_stream(s
);
2567 http_log("Feed '%s' stream number does not match registered feed\n",
2568 c
->stream
->feed_filename
);
2572 for (i
= 0; i
< s
->nb_streams
; i
++) {
2573 AVStream
*fst
= feed
->streams
[i
];
2574 AVStream
*st
= s
->streams
[i
];
2575 memcpy(fst
->codec
, st
->codec
, sizeof(AVCodecContext
));
2576 if (fst
->codec
->extradata_size
) {
2577 fst
->codec
->extradata
= av_malloc(fst
->codec
->extradata_size
);
2578 if (!fst
->codec
->extradata
)
2580 memcpy(fst
->codec
->extradata
, st
->codec
->extradata
,
2581 fst
->codec
->extradata_size
);
2585 av_close_input_stream(s
);
2588 c
->buffer_ptr
= c
->buffer
;
2593 c
->stream
->feed_opened
= 0;
2595 /* wake up any waiting connections to stop waiting for feed */
2596 for(c1
= first_http_ctx
; c1
!= NULL
; c1
= c1
->next
) {
2597 if (c1
->state
== HTTPSTATE_WAIT_FEED
&&
2598 c1
->stream
->feed
== c
->stream
->feed
)
2599 c1
->state
= HTTPSTATE_SEND_DATA_TRAILER
;
2604 /********************************************************************/
2607 static void rtsp_reply_header(HTTPContext
*c
, enum RTSPStatusCode error_number
)
2614 switch(error_number
) {
2615 case RTSP_STATUS_OK
:
2618 case RTSP_STATUS_METHOD
:
2619 str
= "Method Not Allowed";
2621 case RTSP_STATUS_BANDWIDTH
:
2622 str
= "Not Enough Bandwidth";
2624 case RTSP_STATUS_SESSION
:
2625 str
= "Session Not Found";
2627 case RTSP_STATUS_STATE
:
2628 str
= "Method Not Valid in This State";
2630 case RTSP_STATUS_AGGREGATE
:
2631 str
= "Aggregate operation not allowed";
2633 case RTSP_STATUS_ONLY_AGGREGATE
:
2634 str
= "Only aggregate operation allowed";
2636 case RTSP_STATUS_TRANSPORT
:
2637 str
= "Unsupported transport";
2639 case RTSP_STATUS_INTERNAL
:
2640 str
= "Internal Server Error";
2642 case RTSP_STATUS_SERVICE
:
2643 str
= "Service Unavailable";
2645 case RTSP_STATUS_VERSION
:
2646 str
= "RTSP Version not supported";
2649 str
= "Unknown Error";
2653 url_fprintf(c
->pb
, "RTSP/1.0 %d %s\r\n", error_number
, str
);
2654 url_fprintf(c
->pb
, "CSeq: %d\r\n", c
->seq
);
2656 /* output GMT time */
2660 p
= buf2
+ strlen(p
) - 1;
2663 url_fprintf(c
->pb
, "Date: %s GMT\r\n", buf2
);
2666 static void rtsp_reply_error(HTTPContext
*c
, enum RTSPStatusCode error_number
)
2668 rtsp_reply_header(c
, error_number
);
2669 url_fprintf(c
->pb
, "\r\n");
2672 static int rtsp_parse_request(HTTPContext
*c
)
2674 const char *p
, *p1
, *p2
;
2680 RTSPMessageHeader header1
, *header
= &header1
;
2682 c
->buffer_ptr
[0] = '\0';
2685 get_word(cmd
, sizeof(cmd
), &p
);
2686 get_word(url
, sizeof(url
), &p
);
2687 get_word(protocol
, sizeof(protocol
), &p
);
2689 av_strlcpy(c
->method
, cmd
, sizeof(c
->method
));
2690 av_strlcpy(c
->url
, url
, sizeof(c
->url
));
2691 av_strlcpy(c
->protocol
, protocol
, sizeof(c
->protocol
));
2693 if (url_open_dyn_buf(&c
->pb
) < 0) {
2694 /* XXX: cannot do more */
2695 c
->pb
= NULL
; /* safety */
2699 /* check version name */
2700 if (strcmp(protocol
, "RTSP/1.0") != 0) {
2701 rtsp_reply_error(c
, RTSP_STATUS_VERSION
);
2705 /* parse each header line */
2706 memset(header
, 0, sizeof(*header
));
2707 /* skip to next line */
2708 while (*p
!= '\n' && *p
!= '\0')
2712 while (*p
!= '\0') {
2713 p1
= strchr(p
, '\n');
2717 if (p2
> p
&& p2
[-1] == '\r')
2719 /* skip empty line */
2723 if (len
> sizeof(line
) - 1)
2724 len
= sizeof(line
) - 1;
2725 memcpy(line
, p
, len
);
2727 ff_rtsp_parse_line(header
, line
);
2731 /* handle sequence number */
2732 c
->seq
= header
->seq
;
2734 if (!strcmp(cmd
, "DESCRIBE"))
2735 rtsp_cmd_describe(c
, url
);
2736 else if (!strcmp(cmd
, "OPTIONS"))
2737 rtsp_cmd_options(c
, url
);
2738 else if (!strcmp(cmd
, "SETUP"))
2739 rtsp_cmd_setup(c
, url
, header
);
2740 else if (!strcmp(cmd
, "PLAY"))
2741 rtsp_cmd_play(c
, url
, header
);
2742 else if (!strcmp(cmd
, "PAUSE"))
2743 rtsp_cmd_pause(c
, url
, header
);
2744 else if (!strcmp(cmd
, "TEARDOWN"))
2745 rtsp_cmd_teardown(c
, url
, header
);
2747 rtsp_reply_error(c
, RTSP_STATUS_METHOD
);
2750 len
= url_close_dyn_buf(c
->pb
, &c
->pb_buffer
);
2751 c
->pb
= NULL
; /* safety */
2753 /* XXX: cannot do more */
2756 c
->buffer_ptr
= c
->pb_buffer
;
2757 c
->buffer_end
= c
->pb_buffer
+ len
;
2758 c
->state
= RTSPSTATE_SEND_REPLY
;
2762 static int prepare_sdp_description(FFStream
*stream
, uint8_t **pbuffer
,
2763 struct in_addr my_ip
)
2765 AVFormatContext
*avc
;
2766 AVStream avs
[MAX_STREAMS
];
2769 avc
= avformat_alloc_context();
2773 av_metadata_set(&avc
->metadata
, "title",
2774 stream
->title
[0] ? stream
->title
: "No Title");
2775 avc
->nb_streams
= stream
->nb_streams
;
2776 if (stream
->is_multicast
) {
2777 snprintf(avc
->filename
, 1024, "rtp://%s:%d?multicast=1?ttl=%d",
2778 inet_ntoa(stream
->multicast_ip
),
2779 stream
->multicast_port
, stream
->multicast_ttl
);
2782 for(i
= 0; i
< stream
->nb_streams
; i
++) {
2783 avc
->streams
[i
] = &avs
[i
];
2784 avc
->streams
[i
]->codec
= stream
->streams
[i
]->codec
;
2786 *pbuffer
= av_mallocz(2048);
2787 avf_sdp_create(&avc
, 1, *pbuffer
, 2048);
2790 return strlen(*pbuffer
);
2793 static void rtsp_cmd_options(HTTPContext
*c
, const char *url
)
2795 // rtsp_reply_header(c, RTSP_STATUS_OK);
2796 url_fprintf(c
->pb
, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK
, "OK");
2797 url_fprintf(c
->pb
, "CSeq: %d\r\n", c
->seq
);
2798 url_fprintf(c
->pb
, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
2799 url_fprintf(c
->pb
, "\r\n");
2802 static void rtsp_cmd_describe(HTTPContext
*c
, const char *url
)
2808 int content_length
, len
;
2809 struct sockaddr_in my_addr
;
2811 /* find which url is asked */
2812 url_split(NULL
, 0, NULL
, 0, NULL
, 0, NULL
, path1
, sizeof(path1
), url
);
2817 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
2818 if (!stream
->is_feed
&&
2819 stream
->fmt
&& !strcmp(stream
->fmt
->name
, "rtp") &&
2820 !strcmp(path
, stream
->filename
)) {
2824 /* no stream found */
2825 rtsp_reply_error(c
, RTSP_STATUS_SERVICE
); /* XXX: right error ? */
2829 /* prepare the media description in sdp format */
2831 /* get the host IP */
2832 len
= sizeof(my_addr
);
2833 getsockname(c
->fd
, (struct sockaddr
*)&my_addr
, &len
);
2834 content_length
= prepare_sdp_description(stream
, &content
, my_addr
.sin_addr
);
2835 if (content_length
< 0) {
2836 rtsp_reply_error(c
, RTSP_STATUS_INTERNAL
);
2839 rtsp_reply_header(c
, RTSP_STATUS_OK
);
2840 url_fprintf(c
->pb
, "Content-Type: application/sdp\r\n");
2841 url_fprintf(c
->pb
, "Content-Length: %d\r\n", content_length
);
2842 url_fprintf(c
->pb
, "\r\n");
2843 put_buffer(c
->pb
, content
, content_length
);
2846 static HTTPContext
*find_rtp_session(const char *session_id
)
2850 if (session_id
[0] == '\0')
2853 for(c
= first_http_ctx
; c
!= NULL
; c
= c
->next
) {
2854 if (!strcmp(c
->session_id
, session_id
))
2860 static RTSPTransportField
*find_transport(RTSPMessageHeader
*h
, enum RTSPLowerTransport lower_transport
)
2862 RTSPTransportField
*th
;
2865 for(i
=0;i
<h
->nb_transports
;i
++) {
2866 th
= &h
->transports
[i
];
2867 if (th
->lower_transport
== lower_transport
)
2873 static void rtsp_cmd_setup(HTTPContext
*c
, const char *url
,
2874 RTSPMessageHeader
*h
)
2877 int stream_index
, port
;
2882 RTSPTransportField
*th
;
2883 struct sockaddr_in dest_addr
;
2884 RTSPActionServerSetup setup
;
2886 /* find which url is asked */
2887 url_split(NULL
, 0, NULL
, 0, NULL
, 0, NULL
, path1
, sizeof(path1
), url
);
2892 /* now check each stream */
2893 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
2894 if (!stream
->is_feed
&&
2895 stream
->fmt
&& !strcmp(stream
->fmt
->name
, "rtp")) {
2896 /* accept aggregate filenames only if single stream */
2897 if (!strcmp(path
, stream
->filename
)) {
2898 if (stream
->nb_streams
!= 1) {
2899 rtsp_reply_error(c
, RTSP_STATUS_AGGREGATE
);
2906 for(stream_index
= 0; stream_index
< stream
->nb_streams
;
2908 snprintf(buf
, sizeof(buf
), "%s/streamid=%d",
2909 stream
->filename
, stream_index
);
2910 if (!strcmp(path
, buf
))
2915 /* no stream found */
2916 rtsp_reply_error(c
, RTSP_STATUS_SERVICE
); /* XXX: right error ? */
2920 /* generate session id if needed */
2921 if (h
->session_id
[0] == '\0')
2922 snprintf(h
->session_id
, sizeof(h
->session_id
), "%08x%08x",
2923 av_lfg_get(&random_state
), av_lfg_get(&random_state
));
2925 /* find rtp session, and create it if none found */
2926 rtp_c
= find_rtp_session(h
->session_id
);
2928 /* always prefer UDP */
2929 th
= find_transport(h
, RTSP_LOWER_TRANSPORT_UDP
);
2931 th
= find_transport(h
, RTSP_LOWER_TRANSPORT_TCP
);
2933 rtsp_reply_error(c
, RTSP_STATUS_TRANSPORT
);
2938 rtp_c
= rtp_new_connection(&c
->from_addr
, stream
, h
->session_id
,
2939 th
->lower_transport
);
2941 rtsp_reply_error(c
, RTSP_STATUS_BANDWIDTH
);
2945 /* open input stream */
2946 if (open_input_stream(rtp_c
, "") < 0) {
2947 rtsp_reply_error(c
, RTSP_STATUS_INTERNAL
);
2952 /* test if stream is OK (test needed because several SETUP needs
2953 to be done for a given file) */
2954 if (rtp_c
->stream
!= stream
) {
2955 rtsp_reply_error(c
, RTSP_STATUS_SERVICE
);
2959 /* test if stream is already set up */
2960 if (rtp_c
->rtp_ctx
[stream_index
]) {
2961 rtsp_reply_error(c
, RTSP_STATUS_STATE
);
2965 /* check transport */
2966 th
= find_transport(h
, rtp_c
->rtp_protocol
);
2967 if (!th
|| (th
->lower_transport
== RTSP_LOWER_TRANSPORT_UDP
&&
2968 th
->client_port_min
<= 0)) {
2969 rtsp_reply_error(c
, RTSP_STATUS_TRANSPORT
);
2973 /* setup default options */
2974 setup
.transport_option
[0] = '\0';
2975 dest_addr
= rtp_c
->from_addr
;
2976 dest_addr
.sin_port
= htons(th
->client_port_min
);
2979 if (rtp_new_av_stream(rtp_c
, stream_index
, &dest_addr
, c
) < 0) {
2980 rtsp_reply_error(c
, RTSP_STATUS_TRANSPORT
);
2984 /* now everything is OK, so we can send the connection parameters */
2985 rtsp_reply_header(c
, RTSP_STATUS_OK
);
2987 url_fprintf(c
->pb
, "Session: %s\r\n", rtp_c
->session_id
);
2989 switch(rtp_c
->rtp_protocol
) {
2990 case RTSP_LOWER_TRANSPORT_UDP
:
2991 port
= rtp_get_local_port(rtp_c
->rtp_handles
[stream_index
]);
2992 url_fprintf(c
->pb
, "Transport: RTP/AVP/UDP;unicast;"
2993 "client_port=%d-%d;server_port=%d-%d",
2994 th
->client_port_min
, th
->client_port_min
+ 1,
2997 case RTSP_LOWER_TRANSPORT_TCP
:
2998 url_fprintf(c
->pb
, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
2999 stream_index
* 2, stream_index
* 2 + 1);
3004 if (setup
.transport_option
[0] != '\0')
3005 url_fprintf(c
->pb
, ";%s", setup
.transport_option
);
3006 url_fprintf(c
->pb
, "\r\n");
3009 url_fprintf(c
->pb
, "\r\n");
3013 /* find an rtp connection by using the session ID. Check consistency
3015 static HTTPContext
*find_rtp_session_with_url(const char *url
,
3016 const char *session_id
)
3024 rtp_c
= find_rtp_session(session_id
);
3028 /* find which url is asked */
3029 url_split(NULL
, 0, NULL
, 0, NULL
, 0, NULL
, path1
, sizeof(path1
), url
);
3033 if(!strcmp(path
, rtp_c
->stream
->filename
)) return rtp_c
;
3034 for(s
=0; s
<rtp_c
->stream
->nb_streams
; ++s
) {
3035 snprintf(buf
, sizeof(buf
), "%s/streamid=%d",
3036 rtp_c
->stream
->filename
, s
);
3037 if(!strncmp(path
, buf
, sizeof(buf
))) {
3038 // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
3045 static void rtsp_cmd_play(HTTPContext
*c
, const char *url
, RTSPMessageHeader
*h
)
3049 rtp_c
= find_rtp_session_with_url(url
, h
->session_id
);
3051 rtsp_reply_error(c
, RTSP_STATUS_SESSION
);
3055 if (rtp_c
->state
!= HTTPSTATE_SEND_DATA
&&
3056 rtp_c
->state
!= HTTPSTATE_WAIT_FEED
&&
3057 rtp_c
->state
!= HTTPSTATE_READY
) {
3058 rtsp_reply_error(c
, RTSP_STATUS_STATE
);
3062 rtp_c
->state
= HTTPSTATE_SEND_DATA
;
3064 /* now everything is OK, so we can send the connection parameters */
3065 rtsp_reply_header(c
, RTSP_STATUS_OK
);
3067 url_fprintf(c
->pb
, "Session: %s\r\n", rtp_c
->session_id
);
3068 url_fprintf(c
->pb
, "\r\n");
3071 static void rtsp_cmd_pause(HTTPContext
*c
, const char *url
, RTSPMessageHeader
*h
)
3075 rtp_c
= find_rtp_session_with_url(url
, h
->session_id
);
3077 rtsp_reply_error(c
, RTSP_STATUS_SESSION
);
3081 if (rtp_c
->state
!= HTTPSTATE_SEND_DATA
&&
3082 rtp_c
->state
!= HTTPSTATE_WAIT_FEED
) {
3083 rtsp_reply_error(c
, RTSP_STATUS_STATE
);
3087 rtp_c
->state
= HTTPSTATE_READY
;
3088 rtp_c
->first_pts
= AV_NOPTS_VALUE
;
3089 /* now everything is OK, so we can send the connection parameters */
3090 rtsp_reply_header(c
, RTSP_STATUS_OK
);
3092 url_fprintf(c
->pb
, "Session: %s\r\n", rtp_c
->session_id
);
3093 url_fprintf(c
->pb
, "\r\n");
3096 static void rtsp_cmd_teardown(HTTPContext
*c
, const char *url
, RTSPMessageHeader
*h
)
3099 char session_id
[32];
3101 rtp_c
= find_rtp_session_with_url(url
, h
->session_id
);
3103 rtsp_reply_error(c
, RTSP_STATUS_SESSION
);
3107 av_strlcpy(session_id
, rtp_c
->session_id
, sizeof(session_id
));
3109 /* abort the session */
3110 close_connection(rtp_c
);
3112 /* now everything is OK, so we can send the connection parameters */
3113 rtsp_reply_header(c
, RTSP_STATUS_OK
);
3115 url_fprintf(c
->pb
, "Session: %s\r\n", session_id
);
3116 url_fprintf(c
->pb
, "\r\n");
3120 /********************************************************************/
3123 static HTTPContext
*rtp_new_connection(struct sockaddr_in
*from_addr
,
3124 FFStream
*stream
, const char *session_id
,
3125 enum RTSPLowerTransport rtp_protocol
)
3127 HTTPContext
*c
= NULL
;
3128 const char *proto_str
;
3130 /* XXX: should output a warning page when coming
3131 close to the connection limit */
3132 if (nb_connections
>= nb_max_connections
)
3135 /* add a new connection */
3136 c
= av_mallocz(sizeof(HTTPContext
));
3141 c
->poll_entry
= NULL
;
3142 c
->from_addr
= *from_addr
;
3143 c
->buffer_size
= IOBUFFER_INIT_SIZE
;
3144 c
->buffer
= av_malloc(c
->buffer_size
);
3149 av_strlcpy(c
->session_id
, session_id
, sizeof(c
->session_id
));
3150 c
->state
= HTTPSTATE_READY
;
3151 c
->is_packetized
= 1;
3152 c
->rtp_protocol
= rtp_protocol
;
3154 /* protocol is shown in statistics */
3155 switch(c
->rtp_protocol
) {
3156 case RTSP_LOWER_TRANSPORT_UDP_MULTICAST
:
3157 proto_str
= "MCAST";
3159 case RTSP_LOWER_TRANSPORT_UDP
:
3162 case RTSP_LOWER_TRANSPORT_TCP
:
3169 av_strlcpy(c
->protocol
, "RTP/", sizeof(c
->protocol
));
3170 av_strlcat(c
->protocol
, proto_str
, sizeof(c
->protocol
));
3172 current_bandwidth
+= stream
->bandwidth
;
3174 c
->next
= first_http_ctx
;
3186 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3187 command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
3189 static int rtp_new_av_stream(HTTPContext
*c
,
3190 int stream_index
, struct sockaddr_in
*dest_addr
,
3191 HTTPContext
*rtsp_c
)
3193 AVFormatContext
*ctx
;
3196 URLContext
*h
= NULL
;
3198 int max_packet_size
;
3200 /* now we can open the relevant output stream */
3201 ctx
= avformat_alloc_context();
3204 ctx
->oformat
= av_guess_format("rtp", NULL
, NULL
);
3206 st
= av_mallocz(sizeof(AVStream
));
3209 st
->codec
= avcodec_alloc_context();
3210 ctx
->nb_streams
= 1;
3211 ctx
->streams
[0] = st
;
3213 if (!c
->stream
->feed
||
3214 c
->stream
->feed
== c
->stream
)
3215 memcpy(st
, c
->stream
->streams
[stream_index
], sizeof(AVStream
));
3218 c
->stream
->feed
->streams
[c
->stream
->feed_streams
[stream_index
]],
3220 st
->priv_data
= NULL
;
3222 /* build destination RTP address */
3223 ipaddr
= inet_ntoa(dest_addr
->sin_addr
);
3225 switch(c
->rtp_protocol
) {
3226 case RTSP_LOWER_TRANSPORT_UDP
:
3227 case RTSP_LOWER_TRANSPORT_UDP_MULTICAST
:
3230 /* XXX: also pass as parameter to function ? */
3231 if (c
->stream
->is_multicast
) {
3233 ttl
= c
->stream
->multicast_ttl
;
3236 snprintf(ctx
->filename
, sizeof(ctx
->filename
),
3237 "rtp://%s:%d?multicast=1&ttl=%d",
3238 ipaddr
, ntohs(dest_addr
->sin_port
), ttl
);
3240 snprintf(ctx
->filename
, sizeof(ctx
->filename
),
3241 "rtp://%s:%d", ipaddr
, ntohs(dest_addr
->sin_port
));
3244 if (url_open(&h
, ctx
->filename
, URL_WRONLY
) < 0)
3246 c
->rtp_handles
[stream_index
] = h
;
3247 max_packet_size
= url_get_max_packet_size(h
);