2 * Copyright (c) 2000, 2001, 2002 Fabrice Bellard
4 * This file is part of FFmpeg.
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with FFmpeg; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 * multiple format streaming server based on the FFmpeg libraries
28 #define closesocket close
33 #include "libavformat/avformat.h"
34 /* FIXME: those are internal headers, ffserver _really_ shouldn't use them */
35 #include "libavformat/ffm.h"
36 #include "libavformat/network.h"
37 #include "libavformat/os_support.h"
38 #include "libavformat/rtpdec.h"
39 #include "libavformat/rtpproto.h"
40 #include "libavformat/rtsp.h"
41 #include "libavformat/rtspcodes.h"
42 #include "libavformat/avio_internal.h"
43 #include "libavformat/internal.h"
44 #include "libavformat/url.h"
46 #include "libavutil/avassert.h"
47 #include "libavutil/avstring.h"
48 #include "libavutil/lfg.h"
49 #include "libavutil/dict.h"
50 #include "libavutil/intreadwrite.h"
51 #include "libavutil/mathematics.h"
52 #include "libavutil/random_seed.h"
53 #include "libavutil/parseutils.h"
54 #include "libavutil/opt.h"
55 #include "libavutil/time.h"
62 #include <sys/ioctl.h>
72 #include "ffserver_config.h"
74 #define PATH_LENGTH 1024
76 const char program_name
[] = "ffserver";
77 const int program_birth_year
= 2000;
79 static const OptionDef options
[];
82 HTTPSTATE_WAIT_REQUEST
,
83 HTTPSTATE_SEND_HEADER
,
84 HTTPSTATE_SEND_DATA_HEADER
,
85 HTTPSTATE_SEND_DATA
, /* sending TCP or UDP data */
86 HTTPSTATE_SEND_DATA_TRAILER
,
87 HTTPSTATE_RECEIVE_DATA
,
88 HTTPSTATE_WAIT_FEED
, /* wait for data from the feed */
91 RTSPSTATE_WAIT_REQUEST
,
93 RTSPSTATE_SEND_PACKET
,
96 static const char * const http_state
[] = {
112 #define IOBUFFER_INIT_SIZE 8192
114 /* timeouts are in ms */
115 #define HTTP_REQUEST_TIMEOUT (15 * 1000)
116 #define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
118 #define SYNC_TIMEOUT (10 * 1000)
120 typedef struct RTSPActionServerSetup
{
122 char transport_option
[512];
123 } RTSPActionServerSetup
;
126 int64_t count1
, count2
;
127 int64_t time1
, time2
;
130 /* context associated with one connection */
131 typedef struct HTTPContext
{
132 enum HTTPState state
;
133 int fd
; /* socket file descriptor */
134 struct sockaddr_in from_addr
; /* origin */
135 struct pollfd
*poll_entry
; /* used when polling */
137 uint8_t *buffer_ptr
, *buffer_end
;
140 int chunked_encoding
;
141 int chunk_size
; /* 0 if it needs to be read */
142 struct HTTPContext
*next
;
143 int got_key_frame
; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
147 /* input format handling */
148 AVFormatContext
*fmt_in
;
149 int64_t start_time
; /* In milliseconds - this wraps fairly often */
150 int64_t first_pts
; /* initial pts value */
151 int64_t cur_pts
; /* current pts value from the stream in us */
152 int64_t cur_frame_duration
; /* duration of the current frame in us */
153 int cur_frame_bytes
; /* output frame size, needed to compute
154 the time at which we send each
156 int pts_stream_index
; /* stream we choose as clock reference */
157 int64_t cur_clock
; /* current clock reference value in us */
158 /* output format handling */
159 struct FFServerStream
*stream
;
160 /* -1 is invalid stream */
161 int feed_streams
[FFSERVER_MAX_STREAMS
]; /* index of streams in the feed */
162 int switch_feed_streams
[FFSERVER_MAX_STREAMS
]; /* index of streams in the feed */
164 AVFormatContext fmt_ctx
; /* instance of FFServerStream for one user */
165 int last_packet_sent
; /* true if last data packet was sent */
167 DataRateData datarate
;
174 int is_packetized
; /* if true, the stream is packetized */
175 int packet_stream_index
; /* current stream for output in state machine */
177 /* RTSP state specific */
178 uint8_t *pb_buffer
; /* XXX: use that in all the code */
180 int seq
; /* RTSP sequence number */
182 /* RTP state specific */
183 enum RTSPLowerTransport rtp_protocol
;
184 char session_id
[32]; /* session id */
185 AVFormatContext
*rtp_ctx
[FFSERVER_MAX_STREAMS
];
187 /* RTP/UDP specific */
188 URLContext
*rtp_handles
[FFSERVER_MAX_STREAMS
];
190 /* RTP/TCP specific */
191 struct HTTPContext
*rtsp_c
;
192 uint8_t *packet_buffer
, *packet_buffer_ptr
, *packet_buffer_end
;
195 typedef struct FeedData
{
196 long long data_count
;
197 float avg_frame_size
; /* frame size averaged over last frames with exponential mean */
200 static HTTPContext
*first_http_ctx
;
202 static FFServerConfig config
= {
203 .nb_max_http_connections
= 2000,
204 .nb_max_connections
= 5,
205 .max_bandwidth
= 1000,
209 static void new_connection(int server_fd
, int is_rtsp
);
210 static void close_connection(HTTPContext
*c
);
213 static int handle_connection(HTTPContext
*c
);
214 static inline void print_stream_params(AVIOContext
*pb
, FFServerStream
*stream
);
215 static void compute_status(HTTPContext
*c
);
216 static int open_input_stream(HTTPContext
*c
, const char *info
);
217 static int http_parse_request(HTTPContext
*c
);
218 static int http_send_data(HTTPContext
*c
);
219 static int http_start_receive_data(HTTPContext
*c
);
220 static int http_receive_data(HTTPContext
*c
);
223 static int rtsp_parse_request(HTTPContext
*c
);
224 static void rtsp_cmd_describe(HTTPContext
*c
, const char *url
);
225 static void rtsp_cmd_options(HTTPContext
*c
, const char *url
);
226 static void rtsp_cmd_setup(HTTPContext
*c
, const char *url
,
227 RTSPMessageHeader
*h
);
228 static void rtsp_cmd_play(HTTPContext
*c
, const char *url
,
229 RTSPMessageHeader
*h
);
230 static void rtsp_cmd_interrupt(HTTPContext
*c
, const char *url
,
231 RTSPMessageHeader
*h
, int pause_only
);
234 static int prepare_sdp_description(FFServerStream
*stream
, uint8_t **pbuffer
,
235 struct in_addr my_ip
);
238 static HTTPContext
*rtp_new_connection(struct sockaddr_in
*from_addr
,
239 FFServerStream
*stream
,
240 const char *session_id
,
241 enum RTSPLowerTransport rtp_protocol
);
242 static int rtp_new_av_stream(HTTPContext
*c
,
243 int stream_index
, struct sockaddr_in
*dest_addr
,
244 HTTPContext
*rtsp_c
);
246 static size_t htmlencode (const char *src
, char **dest
);
247 static inline void cp_html_entity (char *buffer
, const char *entity
);
248 static inline int check_codec_match(AVCodecContext
*ccf
, AVCodecContext
*ccs
,
251 static const char *my_program_name
;
253 static int no_launch
;
254 static int need_to_start_children
;
256 /* maximum number of simultaneous HTTP connections */
257 static unsigned int nb_connections
;
259 static uint64_t current_bandwidth
;
261 /* Making this global saves on passing it around everywhere */
262 static int64_t cur_time
;
264 static AVLFG random_state
;
266 static FILE *logfile
= NULL
;
268 static inline void cp_html_entity (char *buffer
, const char *entity
) {
269 if (!buffer
|| !entity
)
272 *buffer
++ = *entity
++;
276 * Substitutes known conflicting chars on a text string with
277 * their corresponding HTML entities.
279 * Returns the number of bytes in the 'encoded' representation
280 * not including the terminating NUL.
282 static size_t htmlencode (const char *src
, char **dest
) {
283 const char *amp
= "&";
284 const char *lt
= "<";
285 const char *gt
= ">";
288 size_t final_size
= 0;
295 /* Compute needed dest size */
296 while (*src
!= '\0') {
312 *dest
= av_mallocz(final_size
+ 1);
318 while (*src
!= '\0') {
321 cp_html_entity (tmp
, amp
);
325 cp_html_entity (tmp
, lt
);
329 cp_html_entity (tmp
, gt
);
343 static int64_t ffm_read_write_index(int fd
)
347 if (lseek(fd
, 8, SEEK_SET
) < 0)
349 if (read(fd
, buf
, 8) != 8)
354 static int ffm_write_write_index(int fd
, int64_t pos
)
360 buf
[i
] = (pos
>> (56 - i
* 8)) & 0xff;
361 if (lseek(fd
, 8, SEEK_SET
) < 0)
363 if (write(fd
, buf
, 8) != 8)
372 static void ffm_set_write_index(AVFormatContext
*s
, int64_t pos
,
375 av_opt_set_int(s
, "server_attached", 1, AV_OPT_SEARCH_CHILDREN
);
376 av_opt_set_int(s
, "ffm_write_index", pos
, AV_OPT_SEARCH_CHILDREN
);
377 av_opt_set_int(s
, "ffm_file_size", file_size
, AV_OPT_SEARCH_CHILDREN
);
380 static char *ctime1(char *buf2
, size_t buf_size
)
391 av_strlcpy(buf2
, p
, buf_size
);
392 p
= buf2
+ strlen(buf2
) - 1;
398 static void http_vlog(const char *fmt
, va_list vargs
)
400 static int print_prefix
= 1;
407 ctime1(buf
, sizeof(buf
));
408 fprintf(logfile
, "%s ", buf
);
410 print_prefix
= strstr(fmt
, "\n") != NULL
;
411 vfprintf(logfile
, fmt
, vargs
);
416 __attribute__ ((format (printf
, 1, 2)))
418 static void http_log(const char *fmt
, ...)
421 va_start(vargs
, fmt
);
422 http_vlog(fmt
, vargs
);
426 static void http_av_log(void *ptr
, int level
, const char *fmt
, va_list vargs
)
428 static int print_prefix
= 1;
429 AVClass
*avc
= ptr
? *(AVClass
**)ptr
: NULL
;
430 if (level
> av_log_get_level())
432 if (print_prefix
&& avc
)
433 http_log("[%s @ %p]", avc
->item_name(ptr
), ptr
);
434 print_prefix
= strstr(fmt
, "\n") != NULL
;
435 http_vlog(fmt
, vargs
);
438 static void log_connection(HTTPContext
*c
)
443 http_log("%s - - [%s] \"%s %s\" %d %"PRId64
"\n",
444 inet_ntoa(c
->from_addr
.sin_addr
), c
->method
, c
->url
,
445 c
->protocol
, (c
->http_error
? c
->http_error
: 200), c
->data_count
);
448 static void update_datarate(DataRateData
*drd
, int64_t count
)
450 if (!drd
->time1
&& !drd
->count1
) {
451 drd
->time1
= drd
->time2
= cur_time
;
452 drd
->count1
= drd
->count2
= count
;
453 } else if (cur_time
- drd
->time2
> 5000) {
454 drd
->time1
= drd
->time2
;
455 drd
->count1
= drd
->count2
;
456 drd
->time2
= cur_time
;
461 /* In bytes per second */
462 static int compute_datarate(DataRateData
*drd
, int64_t count
)
464 if (cur_time
== drd
->time1
)
467 return ((count
- drd
->count1
) * 1000) / (cur_time
- drd
->time1
);
471 static void start_children(FFServerStream
*feed
)
481 cmd_length
= strlen(my_program_name
);
484 * FIXME: WIP Safeguard. Remove after clearing all harcoded
485 * '1024' path lengths
487 if (cmd_length
> PATH_LENGTH
- 1) {
488 http_log("Could not start children. Command line: '%s' exceeds "
489 "path length limit (%d)\n", my_program_name
, PATH_LENGTH
);
493 pathname
= av_strdup (my_program_name
);
495 http_log("Could not allocate memory for children cmd line\n");
498 /* replace "ffserver" with "ffmpeg" in the path of current
499 * program. Ignore user provided path */
501 slash
= strrchr(pathname
, '/');
506 strcpy(slash
, "ffmpeg");
508 for (; feed
; feed
= feed
->next
) {
510 if (!feed
->child_argv
|| feed
->pid
)
513 feed
->pid_start
= time(0);
517 http_log("Unable to create children: %s\n", strerror(errno
));
527 http_log("Launch command line: ");
528 http_log("%s ", pathname
);
530 for (i
= 1; feed
->child_argv
[i
] && feed
->child_argv
[i
][0]; i
++)
531 http_log("%s ", feed
->child_argv
[i
]);
534 for (i
= 3; i
< 256; i
++)
538 if (!freopen("/dev/null", "r", stdin
))
539 http_log("failed to redirect STDIN to /dev/null\n;");
540 if (!freopen("/dev/null", "w", stdout
))
541 http_log("failed to redirect STDOUT to /dev/null\n;");
542 if (!freopen("/dev/null", "w", stderr
))
543 http_log("failed to redirect STDERR to /dev/null\n;");
546 signal(SIGPIPE
, SIG_DFL
);
547 execvp(pathname
, feed
->child_argv
);
554 /* open a listening socket */
555 static int socket_open_listen(struct sockaddr_in
*my_addr
)
559 server_fd
= socket(AF_INET
,SOCK_STREAM
,0);
566 if (setsockopt(server_fd
, SOL_SOCKET
, SO_REUSEADDR
, &tmp
, sizeof(tmp
)))
567 av_log(NULL
, AV_LOG_WARNING
, "setsockopt SO_REUSEADDR failed\n");
569 my_addr
->sin_family
= AF_INET
;
570 if (bind (server_fd
, (struct sockaddr
*) my_addr
, sizeof (*my_addr
)) < 0) {
572 snprintf(bindmsg
, sizeof(bindmsg
), "bind(port %d)",
573 ntohs(my_addr
->sin_port
));
578 if (listen (server_fd
, 5) < 0) {
583 if (ff_socket_nonblock(server_fd
, 1) < 0)
584 av_log(NULL
, AV_LOG_WARNING
, "ff_socket_nonblock failed\n");
589 closesocket(server_fd
);
593 /* start all multicast streams */
594 static void start_multicast(void)
596 FFServerStream
*stream
;
599 struct sockaddr_in dest_addr
= {0};
600 int default_port
, stream_index
;
601 unsigned int random0
, random1
;
604 for(stream
= config
.first_stream
; stream
; stream
= stream
->next
) {
606 if (!stream
->is_multicast
)
609 random0
= av_lfg_get(&random_state
);
610 random1
= av_lfg_get(&random_state
);
612 /* open the RTP connection */
613 snprintf(session_id
, sizeof(session_id
), "%08x%08x", random0
, random1
);
615 /* choose a port if none given */
616 if (stream
->multicast_port
== 0) {
617 stream
->multicast_port
= default_port
;
621 dest_addr
.sin_family
= AF_INET
;
622 dest_addr
.sin_addr
= stream
->multicast_ip
;
623 dest_addr
.sin_port
= htons(stream
->multicast_port
);
625 rtp_c
= rtp_new_connection(&dest_addr
, stream
, session_id
,
626 RTSP_LOWER_TRANSPORT_UDP_MULTICAST
);
630 if (open_input_stream(rtp_c
, "") < 0) {
631 http_log("Could not open input stream for stream '%s'\n",
636 /* open each RTP stream */
637 for(stream_index
= 0; stream_index
< stream
->nb_streams
;
639 dest_addr
.sin_port
= htons(stream
->multicast_port
+
641 if (rtp_new_av_stream(rtp_c
, stream_index
, &dest_addr
, NULL
) >= 0)
644 http_log("Could not open output stream '%s/streamid=%d'\n",
645 stream
->filename
, stream_index
);
649 rtp_c
->state
= HTTPSTATE_SEND_DATA
;
653 /* main loop of the HTTP server */
654 static int http_server(void)
656 int server_fd
= 0, rtsp_server_fd
= 0;
658 struct pollfd
*poll_table
, *poll_entry
;
659 HTTPContext
*c
, *c_next
;
661 poll_table
= av_mallocz_array(config
.nb_max_http_connections
+ 2,
662 sizeof(*poll_table
));
664 http_log("Impossible to allocate a poll table handling %d "
665 "connections.\n", config
.nb_max_http_connections
);
669 if (config
.http_addr
.sin_port
) {
670 server_fd
= socket_open_listen(&config
.http_addr
);
675 if (config
.rtsp_addr
.sin_port
) {
676 rtsp_server_fd
= socket_open_listen(&config
.rtsp_addr
);
677 if (rtsp_server_fd
< 0) {
678 closesocket(server_fd
);
683 if (!rtsp_server_fd
&& !server_fd
) {
684 http_log("HTTP and RTSP disabled.\n");
688 http_log("FFserver started.\n");
690 start_children(config
.first_feed
);
695 poll_entry
= poll_table
;
697 poll_entry
->fd
= server_fd
;
698 poll_entry
->events
= POLLIN
;
701 if (rtsp_server_fd
) {
702 poll_entry
->fd
= rtsp_server_fd
;
703 poll_entry
->events
= POLLIN
;
707 /* wait for events on each HTTP handle */
714 case HTTPSTATE_SEND_HEADER
:
715 case RTSPSTATE_SEND_REPLY
:
716 case RTSPSTATE_SEND_PACKET
:
717 c
->poll_entry
= poll_entry
;
719 poll_entry
->events
= POLLOUT
;
722 case HTTPSTATE_SEND_DATA_HEADER
:
723 case HTTPSTATE_SEND_DATA
:
724 case HTTPSTATE_SEND_DATA_TRAILER
:
725 if (!c
->is_packetized
) {
726 /* for TCP, we output as much as we can
727 * (may need to put a limit) */
728 c
->poll_entry
= poll_entry
;
730 poll_entry
->events
= POLLOUT
;
733 /* when ffserver is doing the timing, we work by
734 * looking at which packet needs to be sent every
735 * 10 ms (one tick wait XXX: 10 ms assumed) */
740 case HTTPSTATE_WAIT_REQUEST
:
741 case HTTPSTATE_RECEIVE_DATA
:
742 case HTTPSTATE_WAIT_FEED
:
743 case RTSPSTATE_WAIT_REQUEST
:
744 /* need to catch errors */
745 c
->poll_entry
= poll_entry
;
747 poll_entry
->events
= POLLIN
;/* Maybe this will work */
751 c
->poll_entry
= NULL
;
757 /* wait for an event on one connection. We poll at least every
758 * second to handle timeouts */
760 ret
= poll(poll_table
, poll_entry
- poll_table
, delay
);
761 if (ret
< 0 && ff_neterrno() != AVERROR(EAGAIN
) &&
762 ff_neterrno() != AVERROR(EINTR
)) {
767 cur_time
= av_gettime() / 1000;
769 if (need_to_start_children
) {
770 need_to_start_children
= 0;
771 start_children(config
.first_feed
);
774 /* now handle the events */
775 for(c
= first_http_ctx
; c
; c
= c_next
) {
777 if (handle_connection(c
) < 0) {
779 /* close and free the connection */
784 poll_entry
= poll_table
;
786 /* new HTTP connection request ? */
787 if (poll_entry
->revents
& POLLIN
)
788 new_connection(server_fd
, 0);
791 if (rtsp_server_fd
) {
792 /* new RTSP connection request ? */
793 if (poll_entry
->revents
& POLLIN
)
794 new_connection(rtsp_server_fd
, 1);
803 /* start waiting for a new HTTP/RTSP request */
804 static void start_wait_request(HTTPContext
*c
, int is_rtsp
)
806 c
->buffer_ptr
= c
->buffer
;
807 c
->buffer_end
= c
->buffer
+ c
->buffer_size
- 1; /* leave room for '\0' */
809 c
->state
= is_rtsp
? RTSPSTATE_WAIT_REQUEST
: HTTPSTATE_WAIT_REQUEST
;
810 c
->timeout
= cur_time
+
811 (is_rtsp
? RTSP_REQUEST_TIMEOUT
: HTTP_REQUEST_TIMEOUT
);
814 static void http_send_too_busy_reply(int fd
)
817 int len
= snprintf(buffer
, sizeof(buffer
),
818 "HTTP/1.0 503 Server too busy\r\n"
819 "Content-type: text/html\r\n"
822 "<html><head><title>Too busy</title></head><body>\r\n"
823 "<p>The server is too busy to serve your request at "
825 "<p>The number of current connections is %u, and this "
826 "exceeds the limit of %u.</p>\r\n"
827 "</body></html>\r\n",
828 nb_connections
, config
.nb_max_connections
);
829 av_assert0(len
< sizeof(buffer
));
830 if (send(fd
, buffer
, len
, 0) < len
)
831 av_log(NULL
, AV_LOG_WARNING
,
832 "Could not send too-busy reply, send() failed\n");
836 static void new_connection(int server_fd
, int is_rtsp
)
838 struct sockaddr_in from_addr
;
841 HTTPContext
*c
= NULL
;
843 len
= sizeof(from_addr
);
844 fd
= accept(server_fd
, (struct sockaddr
*)&from_addr
,
847 http_log("error during accept %s\n", strerror(errno
));
850 if (ff_socket_nonblock(fd
, 1) < 0)
851 av_log(NULL
, AV_LOG_WARNING
, "ff_socket_nonblock failed\n");
853 if (nb_connections
>= config
.nb_max_connections
) {
854 http_send_too_busy_reply(fd
);
858 /* add a new connection */
859 c
= av_mallocz(sizeof(HTTPContext
));
864 c
->poll_entry
= NULL
;
865 c
->from_addr
= from_addr
;
866 c
->buffer_size
= IOBUFFER_INIT_SIZE
;
867 c
->buffer
= av_malloc(c
->buffer_size
);
871 c
->next
= first_http_ctx
;
875 start_wait_request(c
, is_rtsp
);
881 av_freep(&c
->buffer
);
887 static void close_connection(HTTPContext
*c
)
889 HTTPContext
**cp
, *c1
;
891 AVFormatContext
*ctx
;
894 /* remove connection from list */
895 cp
= &first_http_ctx
;
904 /* remove references, if any (XXX: do it faster) */
905 for(c1
= first_http_ctx
; c1
; c1
= c1
->next
) {
910 /* remove connection associated resources */
914 /* close each frame parser */
915 for(i
=0;i
<c
->fmt_in
->nb_streams
;i
++) {
916 st
= c
->fmt_in
->streams
[i
];
917 if (st
->codec
->codec
)
918 avcodec_close(st
->codec
);
920 avformat_close_input(&c
->fmt_in
);
923 /* free RTP output streams if any */
926 nb_streams
= c
->stream
->nb_streams
;
928 for(i
=0;i
<nb_streams
;i
++) {
931 av_write_trailer(ctx
);
932 av_dict_free(&ctx
->metadata
);
933 av_freep(&ctx
->streams
[0]);
936 ffurl_close(c
->rtp_handles
[i
]);
941 if (!c
->last_packet_sent
&& c
->state
== HTTPSTATE_SEND_DATA_TRAILER
) {
943 if (ctx
->oformat
&& avio_open_dyn_buf(&ctx
->pb
) >= 0) {
944 av_write_trailer(ctx
);
945 av_freep(&c
->pb_buffer
);
946 avio_close_dyn_buf(ctx
->pb
, &c
->pb_buffer
);
950 for(i
=0; i
<ctx
->nb_streams
; i
++)
951 av_freep(&ctx
->streams
[i
]);
952 av_freep(&ctx
->streams
);
953 av_freep(&ctx
->priv_data
);
955 if (c
->stream
&& !c
->post
&& c
->stream
->stream_type
== STREAM_TYPE_LIVE
)
956 current_bandwidth
-= c
->stream
->bandwidth
;
958 /* signal that there is no feed if we are the feeder socket */
959 if (c
->state
== HTTPSTATE_RECEIVE_DATA
&& c
->stream
) {
960 c
->stream
->feed_opened
= 0;
964 av_freep(&c
->pb_buffer
);
965 av_freep(&c
->packet_buffer
);
966 av_freep(&c
->buffer
);
971 static int handle_connection(HTTPContext
*c
)
977 case HTTPSTATE_WAIT_REQUEST
:
978 case RTSPSTATE_WAIT_REQUEST
:
980 if ((c
->timeout
- cur_time
) < 0)
982 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
))
985 /* no need to read if no events */
986 if (!(c
->poll_entry
->revents
& POLLIN
))
990 if (!(len
= recv(c
->fd
, c
->buffer_ptr
, 1, 0)))
994 if (ff_neterrno() != AVERROR(EAGAIN
) &&
995 ff_neterrno() != AVERROR(EINTR
))
999 /* search for end of request. */
1000 c
->buffer_ptr
+= len
;
1001 ptr
= c
->buffer_ptr
;
1002 if ((ptr
>= c
->buffer
+ 2 && !memcmp(ptr
-2, "\n\n", 2)) ||
1003 (ptr
>= c
->buffer
+ 4 && !memcmp(ptr
-4, "\r\n\r\n", 4))) {
1004 /* request found : parse it and reply */
1005 if (c
->state
== HTTPSTATE_WAIT_REQUEST
)
1006 ret
= http_parse_request(c
);
1008 ret
= rtsp_parse_request(c
);
1012 } else if (ptr
>= c
->buffer_end
) {
1013 /* request too long: cannot do anything */
1015 } else goto read_loop
;
1019 case HTTPSTATE_SEND_HEADER
:
1020 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
))
1023 /* no need to write if no events */
1024 if (!(c
->poll_entry
->revents
& POLLOUT
))
1026 len
= send(c
->fd
, c
->buffer_ptr
, c
->buffer_end
- c
->buffer_ptr
, 0);
1028 if (ff_neterrno() != AVERROR(EAGAIN
) &&
1029 ff_neterrno() != AVERROR(EINTR
)) {
1030 goto close_connection
;
1034 c
->buffer_ptr
+= len
;
1036 c
->stream
->bytes_served
+= len
;
1037 c
->data_count
+= len
;
1038 if (c
->buffer_ptr
>= c
->buffer_end
) {
1039 av_freep(&c
->pb_buffer
);
1040 /* if error, exit */
1043 /* all the buffer was sent : synchronize to the incoming
1045 c
->state
= HTTPSTATE_SEND_DATA_HEADER
;
1046 c
->buffer_ptr
= c
->buffer_end
= c
->buffer
;
1050 case HTTPSTATE_SEND_DATA
:
1051 case HTTPSTATE_SEND_DATA_HEADER
:
1052 case HTTPSTATE_SEND_DATA_TRAILER
:
1053 /* for packetized output, we consider we can always write (the
1054 * input streams set the speed). It may be better to verify
1055 * that we do not rely too much on the kernel queues */
1056 if (!c
->is_packetized
) {
1057 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
))
1060 /* no need to read if no events */
1061 if (!(c
->poll_entry
->revents
& POLLOUT
))
1064 if (http_send_data(c
) < 0)
1066 /* close connection if trailer sent */
1067 if (c
->state
== HTTPSTATE_SEND_DATA_TRAILER
)
1069 /* Check if it is a single jpeg frame 123 */
1070 if (c
->stream
->single_frame
&& c
->data_count
> c
->cur_frame_bytes
&& c
->cur_frame_bytes
> 0) {
1071 close_connection(c
);
1074 case HTTPSTATE_RECEIVE_DATA
:
1075 /* no need to read if no events */
1076 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
))
1078 if (!(c
->poll_entry
->revents
& POLLIN
))
1080 if (http_receive_data(c
) < 0)
1083 case HTTPSTATE_WAIT_FEED
:
1084 /* no need to read if no events */
1085 if (c
->poll_entry
->revents
& (POLLIN
| POLLERR
| POLLHUP
))
1088 /* nothing to do, we'll be waken up by incoming feed packets */
1091 case RTSPSTATE_SEND_REPLY
:
1092 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
))
1093 goto close_connection
;
1094 /* no need to write if no events */
1095 if (!(c
->poll_entry
->revents
& POLLOUT
))
1097 len
= send(c
->fd
, c
->buffer_ptr
, c
->buffer_end
- c
->buffer_ptr
, 0);
1099 if (ff_neterrno() != AVERROR(EAGAIN
) &&
1100 ff_neterrno() != AVERROR(EINTR
)) {
1101 goto close_connection
;
1105 c
->buffer_ptr
+= len
;
1106 c
->data_count
+= len
;
1107 if (c
->buffer_ptr
>= c
->buffer_end
) {
1108 /* all the buffer was sent : wait for a new request */
1109 av_freep(&c
->pb_buffer
);
1110 start_wait_request(c
, 1);
1113 case RTSPSTATE_SEND_PACKET
:
1114 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
)) {
1115 av_freep(&c
->packet_buffer
);
1118 /* no need to write if no events */
1119 if (!(c
->poll_entry
->revents
& POLLOUT
))
1121 len
= send(c
->fd
, c
->packet_buffer_ptr
,
1122 c
->packet_buffer_end
- c
->packet_buffer_ptr
, 0);
1124 if (ff_neterrno() != AVERROR(EAGAIN
) &&
1125 ff_neterrno() != AVERROR(EINTR
)) {
1126 /* error : close connection */
1127 av_freep(&c
->packet_buffer
);
1132 c
->packet_buffer_ptr
+= len
;
1133 if (c
->packet_buffer_ptr
>= c
->packet_buffer_end
) {
1134 /* all the buffer was sent : wait for a new request */
1135 av_freep(&c
->packet_buffer
);
1136 c
->state
= RTSPSTATE_WAIT_REQUEST
;
1139 case HTTPSTATE_READY
:
1148 av_freep(&c
->pb_buffer
);
1152 static int extract_rates(char *rates
, int ratelen
, const char *request
)
1156 for (p
= request
; *p
&& *p
!= '\r' && *p
!= '\n'; ) {
1157 if (av_strncasecmp(p
, "Pragma:", 7) == 0) {
1158 const char *q
= p
+ 7;
1160 while (*q
&& *q
!= '\n' && av_isspace(*q
))
1163 if (av_strncasecmp(q
, "stream-switch-entry=", 20) == 0) {
1169 memset(rates
, 0xff, ratelen
);
1172 while (*q
&& *q
!= '\n' && *q
!= ':')
1175 if (sscanf(q
, ":%d:%d", &stream_no
, &rate_no
) != 2)
1179 if (stream_no
< ratelen
&& stream_no
>= 0)
1180 rates
[stream_no
] = rate_no
;
1182 while (*q
&& *q
!= '\n' && !av_isspace(*q
))
1189 p
= strchr(p
, '\n');
1199 static int find_stream_in_feed(FFServerStream
*feed
, AVCodecContext
*codec
,
1203 int best_bitrate
= 100000000;
1206 for (i
= 0; i
< feed
->nb_streams
; i
++) {
1207 AVCodecContext
*feed_codec
= feed
->streams
[i
]->codec
;
1209 if (feed_codec
->codec_id
!= codec
->codec_id
||
1210 feed_codec
->sample_rate
!= codec
->sample_rate
||
1211 feed_codec
->width
!= codec
->width
||
1212 feed_codec
->height
!= codec
->height
)
1215 /* Potential stream */
1217 /* We want the fastest stream less than bit_rate, or the slowest
1218 * faster than bit_rate
1221 if (feed_codec
->bit_rate
<= bit_rate
) {
1222 if (best_bitrate
> bit_rate
||
1223 feed_codec
->bit_rate
> best_bitrate
) {
1224 best_bitrate
= feed_codec
->bit_rate
;
1229 if (feed_codec
->bit_rate
< best_bitrate
) {
1230 best_bitrate
= feed_codec
->bit_rate
;
1237 static int modify_current_stream(HTTPContext
*c
, char *rates
)
1240 FFServerStream
*req
= c
->stream
;
1241 int action_required
= 0;
1243 /* Not much we can do for a feed */
1247 for (i
= 0; i
< req
->nb_streams
; i
++) {
1248 AVCodecContext
*codec
= req
->streams
[i
]->codec
;
1252 c
->switch_feed_streams
[i
] = req
->feed_streams
[i
];
1255 c
->switch_feed_streams
[i
] = find_stream_in_feed(req
->feed
, codec
, codec
->bit_rate
/ 2);
1258 /* Wants off or slow */
1259 c
->switch_feed_streams
[i
] = find_stream_in_feed(req
->feed
, codec
, codec
->bit_rate
/ 4);
1261 /* This doesn't work well when it turns off the only stream! */
1262 c
->switch_feed_streams
[i
] = -2;
1263 c
->feed_streams
[i
] = -2;
1268 if (c
->switch_feed_streams
[i
] >= 0 &&
1269 c
->switch_feed_streams
[i
] != c
->feed_streams
[i
]) {
1270 action_required
= 1;
1274 return action_required
;
1277 static void get_word(char *buf
, int buf_size
, const char **pp
)
1283 p
+= strspn(p
, SPACE_CHARS
);
1285 while (!av_isspace(*p
) && *p
!= '\0') {
1286 if ((q
- buf
) < buf_size
- 1)
1295 static FFServerIPAddressACL
* parse_dynamic_acl(FFServerStream
*stream
,
1301 FFServerIPAddressACL
*acl
= NULL
;
1305 f
= fopen(stream
->dynamic_acl
, "r");
1307 perror(stream
->dynamic_acl
);
1311 acl
= av_mallocz(sizeof(FFServerIPAddressACL
));
1318 while (fgets(line
, sizeof(line
), f
)) {
1321 while (av_isspace(*p
))
1323 if (*p
== '\0' || *p
== '#')
1325 ffserver_get_arg(cmd
, sizeof(cmd
), &p
);
1327 if (!av_strcasecmp(cmd
, "ACL"))
1328 ffserver_parse_acl_row(NULL
, NULL
, acl
, p
, stream
->dynamic_acl
,
1336 static void free_acl_list(FFServerIPAddressACL
*in_acl
)
1338 FFServerIPAddressACL
*pacl
, *pacl2
;
1348 static int validate_acl_list(FFServerIPAddressACL
*in_acl
, HTTPContext
*c
)
1350 enum FFServerIPAddressAction last_action
= IP_DENY
;
1351 FFServerIPAddressACL
*acl
;
1352 struct in_addr
*src
= &c
->from_addr
.sin_addr
;
1353 unsigned long src_addr
= src
->s_addr
;
1355 for (acl
= in_acl
; acl
; acl
= acl
->next
) {
1356 if (src_addr
>= acl
->first
.s_addr
&& src_addr
<= acl
->last
.s_addr
)
1357 return (acl
->action
== IP_ALLOW
) ? 1 : 0;
1358 last_action
= acl
->action
;
1361 /* Nothing matched, so return not the last action */
1362 return (last_action
== IP_DENY
) ? 1 : 0;
1365 static int validate_acl(FFServerStream
*stream
, HTTPContext
*c
)
1368 FFServerIPAddressACL
*acl
;
1370 /* if stream->acl is null validate_acl_list will return 1 */
1371 ret
= validate_acl_list(stream
->acl
, c
);
1373 if (stream
->dynamic_acl
[0]) {
1374 acl
= parse_dynamic_acl(stream
, c
);
1375 ret
= validate_acl_list(acl
, c
);
1383 * compute the real filename of a file by matching it without its
1384 * extensions to all the stream's filenames
1386 static void compute_real_filename(char *filename
, int max_size
)
1391 FFServerStream
*stream
;
1393 av_strlcpy(file1
, filename
, sizeof(file1
));
1394 p
= strrchr(file1
, '.');
1397 for(stream
= config
.first_stream
; stream
; stream
= stream
->next
) {
1398 av_strlcpy(file2
, stream
->filename
, sizeof(file2
));
1399 p
= strrchr(file2
, '.');
1402 if (!strcmp(file1
, file2
)) {
1403 av_strlcpy(filename
, stream
->filename
, max_size
);
1418 /* parse HTTP request and prepare header */
1419 static int http_parse_request(HTTPContext
*c
)
1423 enum RedirType redir_type
;
1425 char info
[1024], filename
[1024];
1429 char *encoded_msg
= NULL
;
1430 const char *mime_type
;
1431 FFServerStream
*stream
;
1434 const char *useragent
= 0;
1437 get_word(cmd
, sizeof(cmd
), &p
);
1438 av_strlcpy(c
->method
, cmd
, sizeof(c
->method
));
1440 if (!strcmp(cmd
, "GET"))
1442 else if (!strcmp(cmd
, "POST"))
1447 get_word(url
, sizeof(url
), &p
);
1448 av_strlcpy(c
->url
, url
, sizeof(c
->url
));
1450 get_word(protocol
, sizeof(protocol
), (const char **)&p
);
1451 if (strcmp(protocol
, "HTTP/1.0") && strcmp(protocol
, "HTTP/1.1"))
1454 av_strlcpy(c
->protocol
, protocol
, sizeof(c
->protocol
));
1457 http_log("%s - - New connection: %s %s\n",
1458 inet_ntoa(c
->from_addr
.sin_addr
), cmd
, url
);
1460 /* find the filename and the optional info string in the request */
1461 p1
= strchr(url
, '?');
1463 av_strlcpy(info
, p1
, sizeof(info
));
1468 av_strlcpy(filename
, url
+ ((*url
== '/') ? 1 : 0), sizeof(filename
)-1);
1470 for (p
= c
->buffer
; *p
&& *p
!= '\r' && *p
!= '\n'; ) {
1471 if (av_strncasecmp(p
, "User-Agent:", 11) == 0) {
1473 if (*useragent
&& *useragent
!= '\n' && av_isspace(*useragent
))
1477 p
= strchr(p
, '\n');
1484 redir_type
= REDIR_NONE
;
1485 if (av_match_ext(filename
, "asx")) {
1486 redir_type
= REDIR_ASX
;
1487 filename
[strlen(filename
)-1] = 'f';
1488 } else if (av_match_ext(filename
, "asf") &&
1489 (!useragent
|| av_strncasecmp(useragent
, "NSPlayer", 8))) {
1490 /* if this isn't WMP or lookalike, return the redirector file */
1491 redir_type
= REDIR_ASF
;
1492 } else if (av_match_ext(filename
, "rpm,ram")) {
1493 redir_type
= REDIR_RAM
;
1494 strcpy(filename
+ strlen(filename
)-2, "m");
1495 } else if (av_match_ext(filename
, "rtsp")) {
1496 redir_type
= REDIR_RTSP
;
1497 compute_real_filename(filename
, sizeof(filename
) - 1);
1498 } else if (av_match_ext(filename
, "sdp")) {
1499 redir_type
= REDIR_SDP
;
1500 compute_real_filename(filename
, sizeof(filename
) - 1);
1503 /* "redirect" request to index.html */
1504 if (!strlen(filename
))
1505 av_strlcpy(filename
, "index.html", sizeof(filename
) - 1);
1507 stream
= config
.first_stream
;
1509 if (!strcmp(stream
->filename
, filename
) && validate_acl(stream
, c
))
1511 stream
= stream
->next
;
1514 snprintf(msg
, sizeof(msg
), "File '%s' not found", url
);
1515 http_log("File '%s' not found\n", url
);
1520 memcpy(c
->feed_streams
, stream
->feed_streams
, sizeof(c
->feed_streams
));
1521 memset(c
->switch_feed_streams
, -1, sizeof(c
->switch_feed_streams
));
1523 if (stream
->stream_type
== STREAM_TYPE_REDIRECT
) {
1524 c
->http_error
= 301;
1526 snprintf(q
, c
->buffer_size
,
1527 "HTTP/1.0 301 Moved\r\n"
1529 "Content-type: text/html\r\n"
1532 "<html><head><title>Moved</title></head><body>\r\n"
1533 "You should be <a href=\"%s\">redirected</a>.\r\n"
1534 "</body></html>\r\n",
1535 stream
->feed_filename
, stream
->feed_filename
);
1537 /* prepare output buffer */
1538 c
->buffer_ptr
= c
->buffer
;
1540 c
->state
= HTTPSTATE_SEND_HEADER
;
1544 /* If this is WMP, get the rate information */
1545 if (extract_rates(ratebuf
, sizeof(ratebuf
), c
->buffer
)) {
1546 if (modify_current_stream(c
, ratebuf
)) {
1547 for (i
= 0; i
< FF_ARRAY_ELEMS(c
->feed_streams
); i
++) {
1548 if (c
->switch_feed_streams
[i
] >= 0)
1549 c
->switch_feed_streams
[i
] = -1;
1554 if (c
->post
== 0 && stream
->stream_type
== STREAM_TYPE_LIVE
)
1555 current_bandwidth
+= stream
->bandwidth
;
1557 /* If already streaming this feed, do not let another feeder start */
1558 if (stream
->feed_opened
) {
1559 snprintf(msg
, sizeof(msg
), "This feed is already being received.");
1560 http_log("Feed '%s' already being received\n", stream
->feed_filename
);
1564 if (c
->post
== 0 && config
.max_bandwidth
< current_bandwidth
) {
1565 c
->http_error
= 503;
1567 snprintf(q
, c
->buffer_size
,
1568 "HTTP/1.0 503 Server too busy\r\n"
1569 "Content-type: text/html\r\n"
1572 "<html><head><title>Too busy</title></head><body>\r\n"
1573 "<p>The server is too busy to serve your request at "
1574 "this time.</p>\r\n"
1575 "<p>The bandwidth being served (including your stream) "
1576 "is %"PRIu64
"kbit/s, and this exceeds the limit of "
1577 "%"PRIu64
"kbit/s.</p>\r\n"
1578 "</body></html>\r\n",
1579 current_bandwidth
, config
.max_bandwidth
);
1581 /* prepare output buffer */
1582 c
->buffer_ptr
= c
->buffer
;
1584 c
->state
= HTTPSTATE_SEND_HEADER
;
1588 if (redir_type
!= REDIR_NONE
) {
1589 const char *hostinfo
= 0;
1591 for (p
= c
->buffer
; *p
&& *p
!= '\r' && *p
!= '\n'; ) {
1592 if (av_strncasecmp(p
, "Host:", 5) == 0) {
1596 p
= strchr(p
, '\n');
1607 while (av_isspace(*hostinfo
))
1610 eoh
= strchr(hostinfo
, '\n');
1612 if (eoh
[-1] == '\r')
1615 if (eoh
- hostinfo
< sizeof(hostbuf
) - 1) {
1616 memcpy(hostbuf
, hostinfo
, eoh
- hostinfo
);
1617 hostbuf
[eoh
- hostinfo
] = 0;
1619 c
->http_error
= 200;
1621 switch(redir_type
) {
1623 snprintf(q
, c
->buffer_size
,
1624 "HTTP/1.0 200 ASX Follows\r\n"
1625 "Content-type: video/x-ms-asf\r\n"
1627 "<ASX Version=\"3\">\r\n"
1628 //"<!-- Autogenerated by ffserver -->\r\n"
1629 "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n"
1630 "</ASX>\r\n", hostbuf
, filename
, info
);
1634 snprintf(q
, c
->buffer_size
,
1635 "HTTP/1.0 200 RAM Follows\r\n"
1636 "Content-type: audio/x-pn-realaudio\r\n"
1638 "# Autogenerated by ffserver\r\n"
1639 "http://%s/%s%s\r\n", hostbuf
, filename
, info
);
1643 snprintf(q
, c
->buffer_size
,
1644 "HTTP/1.0 200 ASF Redirect follows\r\n"
1645 "Content-type: video/x-ms-asf\r\n"
1648 "Ref1=http://%s/%s%s\r\n", hostbuf
, filename
, info
);
1653 char hostname
[256], *p
;
1654 /* extract only hostname */
1655 av_strlcpy(hostname
, hostbuf
, sizeof(hostname
));
1656 p
= strrchr(hostname
, ':');
1659 snprintf(q
, c
->buffer_size
,
1660 "HTTP/1.0 200 RTSP Redirect follows\r\n"
1661 /* XXX: incorrect MIME type ? */
1662 "Content-type: application/x-rtsp\r\n"
1664 "rtsp://%s:%d/%s\r\n", hostname
, ntohs(config
.rtsp_addr
.sin_port
), filename
);
1673 struct sockaddr_in my_addr
;
1675 snprintf(q
, c
->buffer_size
,
1676 "HTTP/1.0 200 OK\r\n"
1677 "Content-type: application/sdp\r\n"
1681 len
= sizeof(my_addr
);
1683 /* XXX: Should probably fail? */
1684 if (getsockname(c
->fd
, (struct sockaddr
*)&my_addr
, &len
))
1685 http_log("getsockname() failed\n");
1687 /* XXX: should use a dynamic buffer */
1688 sdp_data_size
= prepare_sdp_description(stream
,
1691 if (sdp_data_size
> 0) {
1692 memcpy(q
, sdp_data
, sdp_data_size
);
1704 /* prepare output buffer */
1705 c
->buffer_ptr
= c
->buffer
;
1707 c
->state
= HTTPSTATE_SEND_HEADER
;
1713 snprintf(msg
, sizeof(msg
), "ASX/RAM file not handled");
1717 stream
->conns_served
++;
1719 /* XXX: add there authenticate and IP match */
1722 /* if post, it means a feed is being sent */
1723 if (!stream
->is_feed
) {
1724 /* However it might be a status report from WMP! Let us log the
1725 * data as it might come handy one day. */
1726 const char *logline
= 0;
1729 for (p
= c
->buffer
; *p
&& *p
!= '\r' && *p
!= '\n'; ) {
1730 if (av_strncasecmp(p
, "Pragma: log-line=", 17) == 0) {
1734 if (av_strncasecmp(p
, "Pragma: client-id=", 18) == 0)
1735 client_id
= strtol(p
+ 18, 0, 10);
1736 p
= strchr(p
, '\n');
1744 char *eol
= strchr(logline
, '\n');
1749 if (eol
[-1] == '\r')
1751 http_log("%.*s\n", (int) (eol
- logline
), logline
);
1752 c
->suppress_log
= 1;
1757 http_log("\nGot request:\n%s\n", c
->buffer
);
1760 if (client_id
&& extract_rates(ratebuf
, sizeof(ratebuf
), c
->buffer
)) {
1763 /* Now we have to find the client_id */
1764 for (wmpc
= first_http_ctx
; wmpc
; wmpc
= wmpc
->next
) {
1765 if (wmpc
->wmp_client_id
== client_id
)
1769 if (wmpc
&& modify_current_stream(wmpc
, ratebuf
))
1770 wmpc
->switch_pending
= 1;
1773 snprintf(msg
, sizeof(msg
), "POST command not handled");
1777 if (http_start_receive_data(c
) < 0) {
1778 snprintf(msg
, sizeof(msg
), "could not open feed");
1782 c
->state
= HTTPSTATE_RECEIVE_DATA
;
1787 if (strcmp(stream
->filename
+ strlen(stream
->filename
) - 4, ".asf") == 0)
1788 http_log("\nGot request:\n%s\n", c
->buffer
);
1791 if (c
->stream
->stream_type
== STREAM_TYPE_STATUS
)
1794 /* open input stream */
1795 if (open_input_stream(c
, info
) < 0) {
1796 snprintf(msg
, sizeof(msg
), "Input stream corresponding to '%s' not found", url
);
1800 /* prepare HTTP header */
1802 av_strlcatf(c
->buffer
, c
->buffer_size
, "HTTP/1.0 200 OK\r\n");
1803 mime_type
= c
->stream
->fmt
->mime_type
;
1805 mime_type
= "application/x-octet-stream";
1806 av_strlcatf(c
->buffer
, c
->buffer_size
, "Pragma: no-cache\r\n");
1808 /* for asf, we need extra headers */
1809 if (!strcmp(c
->stream
->fmt
->name
,"asf_stream")) {
1810 /* Need to allocate a client id */
1812 c
->wmp_client_id
= av_lfg_get(&random_state
);
1814 av_strlcatf(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
);
1816 av_strlcatf(c
->buffer
, c
->buffer_size
, "Content-Type: %s\r\n", mime_type
);
1817 av_strlcatf(c
->buffer
, c
->buffer_size
, "\r\n");
1818 q
= c
->buffer
+ strlen(c
->buffer
);
1820 /* prepare output buffer */
1822 c
->buffer_ptr
= c
->buffer
;
1824 c
->state
= HTTPSTATE_SEND_HEADER
;
1827 c
->http_error
= 404;
1829 if (!htmlencode(msg
, &encoded_msg
)) {
1830 http_log("Could not encode filename '%s' as HTML\n", msg
);
1832 snprintf(q
, c
->buffer_size
,
1833 "HTTP/1.0 404 Not Found\r\n"
1834 "Content-type: text/html\r\n"
1839 "<meta charset=\"UTF-8\">\n"
1840 "<title>404 Not Found</title>\n"
1843 "</html>\n", encoded_msg
? encoded_msg
: "File not found");
1845 /* prepare output buffer */
1846 c
->buffer_ptr
= c
->buffer
;
1848 c
->state
= HTTPSTATE_SEND_HEADER
;
1849 av_freep(&encoded_msg
);
1853 /* horrible: we use this value to avoid
1854 * going to the send data state */
1855 c
->http_error
= 200;
1856 c
->state
= HTTPSTATE_SEND_HEADER
;
1860 static void fmt_bytecount(AVIOContext
*pb
, int64_t count
)
1862 static const char suffix
[] = " kMGTP";
1865 for (s
= suffix
; count
>= 100000 && s
[1]; count
/= 1000, s
++);
1867 avio_printf(pb
, "%"PRId64
"%c", count
, *s
);
1870 static inline void print_stream_params(AVIOContext
*pb
, FFServerStream
*stream
)
1873 const char *type
= "unknown";
1874 char parameters
[64];
1878 stream_no
= stream
->nb_streams
;
1880 avio_printf(pb
, "<table cellspacing=0 cellpadding=4><tr><th>Stream<th>"
1881 "type<th>kbit/s<th align=left>codec<th align=left>"
1884 for (i
= 0; i
< stream_no
; i
++) {
1885 st
= stream
->streams
[i
];
1886 codec
= avcodec_find_encoder(st
->codec
->codec_id
);
1890 switch(st
->codec
->codec_type
) {
1891 case AVMEDIA_TYPE_AUDIO
:
1893 snprintf(parameters
, sizeof(parameters
), "%d channel(s), %d Hz",
1894 st
->codec
->channels
, st
->codec
->sample_rate
);
1896 case AVMEDIA_TYPE_VIDEO
:
1898 snprintf(parameters
, sizeof(parameters
),
1899 "%dx%d, q=%d-%d, fps=%d", st
->codec
->width
,
1900 st
->codec
->height
, st
->codec
->qmin
, st
->codec
->qmax
,
1901 st
->codec
->time_base
.den
/ st
->codec
->time_base
.num
);
1907 avio_printf(pb
, "<tr><td align=right>%d<td>%s<td align=right>%"PRId64
1909 i
, type
, (int64_t)st
->codec
->bit_rate
/1000,
1910 codec
? codec
->name
: "", parameters
);
1913 avio_printf(pb
, "</table>\n");
1916 static void compute_status(HTTPContext
*c
)
1919 FFServerStream
*stream
;
1925 if (avio_open_dyn_buf(&pb
) < 0) {
1926 /* XXX: return an error ? */
1927 c
->buffer_ptr
= c
->buffer
;
1928 c
->buffer_end
= c
->buffer
;
1932 avio_printf(pb
, "HTTP/1.0 200 OK\r\n");
1933 avio_printf(pb
, "Content-type: text/html\r\n");
1934 avio_printf(pb
, "Pragma: no-cache\r\n");
1935 avio_printf(pb
, "\r\n");
1937 avio_printf(pb
, "<!DOCTYPE html>\n");
1938 avio_printf(pb
, "<html><head><title>%s Status</title>\n", program_name
);
1939 if (c
->stream
->feed_filename
[0])
1940 avio_printf(pb
, "<link rel=\"shortcut icon\" href=\"%s\">\n",
1941 c
->stream
->feed_filename
);
1942 avio_printf(pb
, "</head>\n<body>");
1943 avio_printf(pb
, "<h1>%s Status</h1>\n", program_name
);
1945 avio_printf(pb
, "<h2>Available Streams</h2>\n");
1946 avio_printf(pb
, "<table cellspacing=0 cellpadding=4>\n");
1947 avio_printf(pb
, "<tr><th valign=top>Path<th align=left>Served<br>Conns<th><br>bytes<th valign=top>Format<th>Bit rate<br>kbit/s<th align=left>Video<br>kbit/s<th><br>Codec<th align=left>Audio<br>kbit/s<th><br>Codec<th align=left valign=top>Feed\n");
1948 stream
= config
.first_stream
;
1950 char sfilename
[1024];
1953 if (stream
->feed
== stream
) {
1954 stream
= stream
->next
;
1958 av_strlcpy(sfilename
, stream
->filename
, sizeof(sfilename
) - 10);
1959 eosf
= sfilename
+ strlen(sfilename
);
1960 if (eosf
- sfilename
>= 4) {
1961 if (strcmp(eosf
- 4, ".asf") == 0)
1962 strcpy(eosf
- 4, ".asx");
1963 else if (strcmp(eosf
- 3, ".rm") == 0)
1964 strcpy(eosf
- 3, ".ram");
1965 else if (stream
->fmt
&& !strcmp(stream
->fmt
->name
, "rtp")) {
1966 /* generate a sample RTSP director if
1967 * unicast. Generate an SDP redirector if
1969 eosf
= strrchr(sfilename
, '.');
1971 eosf
= sfilename
+ strlen(sfilename
);
1972 if (stream
->is_multicast
)
1973 strcpy(eosf
, ".sdp");
1975 strcpy(eosf
, ".rtsp");
1979 avio_printf(pb
, "<tr><td><a href=\"/%s\">%s</a> ",
1980 sfilename
, stream
->filename
);
1981 avio_printf(pb
, "<td align=right> %d <td align=right> ",
1982 stream
->conns_served
);
1983 fmt_bytecount(pb
, stream
->bytes_served
);
1985 switch(stream
->stream_type
) {
1986 case STREAM_TYPE_LIVE
: {
1987 int audio_bit_rate
= 0;
1988 int video_bit_rate
= 0;
1989 const char *audio_codec_name
= "";
1990 const char *video_codec_name
= "";
1991 const char *audio_codec_name_extra
= "";
1992 const char *video_codec_name_extra
= "";
1994 for(i
=0;i
<stream
->nb_streams
;i
++) {
1995 AVStream
*st
= stream
->streams
[i
];
1996 AVCodec
*codec
= avcodec_find_encoder(st
->codec
->codec_id
);
1998 switch(st
->codec
->codec_type
) {
1999 case AVMEDIA_TYPE_AUDIO
:
2000 audio_bit_rate
+= st
->codec
->bit_rate
;
2002 if (*audio_codec_name
)
2003 audio_codec_name_extra
= "...";
2004 audio_codec_name
= codec
->name
;
2007 case AVMEDIA_TYPE_VIDEO
:
2008 video_bit_rate
+= st
->codec
->bit_rate
;
2010 if (*video_codec_name
)
2011 video_codec_name_extra
= "...";
2012 video_codec_name
= codec
->name
;
2015 case AVMEDIA_TYPE_DATA
:
2016 video_bit_rate
+= st
->codec
->bit_rate
;
2023 avio_printf(pb
, "<td align=center> %s <td align=right> %d "
2024 "<td align=right> %d <td> %s %s <td align=right> "
2026 stream
->fmt
->name
, stream
->bandwidth
,
2027 video_bit_rate
/ 1000, video_codec_name
,
2028 video_codec_name_extra
, audio_bit_rate
/ 1000,
2029 audio_codec_name
, audio_codec_name_extra
);
2032 avio_printf(pb
, "<td>%s", stream
->feed
->filename
);
2034 avio_printf(pb
, "<td>%s", stream
->feed_filename
);
2035 avio_printf(pb
, "\n");
2039 avio_printf(pb
, "<td align=center> - <td align=right> - "
2040 "<td align=right> - <td><td align=right> - <td>\n");
2043 stream
= stream
->next
;
2045 avio_printf(pb
, "</table>\n");
2047 stream
= config
.first_stream
;
2050 if (stream
->feed
!= stream
) {
2051 stream
= stream
->next
;
2055 avio_printf(pb
, "<h2>Feed %s</h2>", stream
->filename
);
2057 avio_printf(pb
, "Running as pid %"PRId64
".\n", (int64_t) stream
->pid
);
2064 /* This is somewhat linux specific I guess */
2065 snprintf(ps_cmd
, sizeof(ps_cmd
),
2066 "ps -o \"%%cpu,cputime\" --no-headers %"PRId64
"",
2067 (int64_t) stream
->pid
);
2069 pid_stat
= popen(ps_cmd
, "r");
2074 if (fscanf(pid_stat
, "%9s %63s", cpuperc
, cpuused
) == 2) {
2075 avio_printf(pb
, "Currently using %s%% of the cpu. "
2076 "Total time used %s.\n",
2084 avio_printf(pb
, "<p>");
2087 print_stream_params(pb
, stream
);
2088 stream
= stream
->next
;
2091 /* connection status */
2092 avio_printf(pb
, "<h2>Connection Status</h2>\n");
2094 avio_printf(pb
, "Number of connections: %d / %d<br>\n",
2095 nb_connections
, config
.nb_max_connections
);
2097 avio_printf(pb
, "Bandwidth in use: %"PRIu64
"k / %"PRIu64
"k<br>\n",
2098 current_bandwidth
, config
.max_bandwidth
);
2100 avio_printf(pb
, "<table>\n");
2101 avio_printf(pb
, "<tr><th>#<th>File<th>IP<th>Proto<th>State<th>Target "
2102 "bit/s<th>Actual bit/s<th>Bytes transferred\n");
2103 c1
= first_http_ctx
;
2111 for (j
= 0; j
< c1
->stream
->nb_streams
; j
++) {
2112 if (!c1
->stream
->feed
)
2113 bitrate
+= c1
->stream
->streams
[j
]->codec
->bit_rate
;
2114 else if (c1
->feed_streams
[j
] >= 0)
2115 bitrate
+= c1
->stream
->feed
->streams
[c1
->feed_streams
[j
]]->codec
->bit_rate
;
2120 p
= inet_ntoa(c1
->from_addr
.sin_addr
);
2121 avio_printf(pb
, "<tr><td><b>%d</b><td>%s%s<td>%s<td>%s<td>%s"
2123 i
, c1
->stream
? c1
->stream
->filename
: "",
2124 c1
->state
== HTTPSTATE_RECEIVE_DATA
? "(input)" : "", p
,
2125 c1
->protocol
, http_state
[c1
->state
]);
2126 fmt_bytecount(pb
, bitrate
);
2127 avio_printf(pb
, "<td align=right>");
2128 fmt_bytecount(pb
, compute_datarate(&c1
->datarate
, c1
->data_count
) * 8);
2129 avio_printf(pb
, "<td align=right>");
2130 fmt_bytecount(pb
, c1
->data_count
);
2131 avio_printf(pb
, "\n");
2134 avio_printf(pb
, "</table>\n");
2139 avio_printf(pb
, "<hr size=1 noshade>Generated at %s", p
);
2140 avio_printf(pb
, "</body>\n</html>\n");
2142 len
= avio_close_dyn_buf(pb
, &c
->pb_buffer
);
2143 c
->buffer_ptr
= c
->pb_buffer
;
2144 c
->buffer_end
= c
->pb_buffer
+ len
;
2147 static int open_input_stream(HTTPContext
*c
, const char *info
)
2150 char input_filename
[1024];
2151 AVFormatContext
*s
= NULL
;
2152 int buf_size
, i
, ret
;
2155 /* find file name */
2156 if (c
->stream
->feed
) {
2157 strcpy(input_filename
, c
->stream
->feed
->feed_filename
);
2158 buf_size
= FFM_PACKET_SIZE
;
2159 /* compute position (absolute time) */
2160 if (av_find_info_tag(buf
, sizeof(buf
), "date", info
)) {
2161 if ((ret
= av_parse_time(&stream_pos
, buf
, 0)) < 0) {
2162 http_log("Invalid date specification '%s' for stream\n", buf
);
2165 } else if (av_find_info_tag(buf
, sizeof(buf
), "buffer", info
)) {
2166 int prebuffer
= strtol(buf
, 0, 10);
2167 stream_pos
= av_gettime() - prebuffer
* (int64_t)1000000;
2169 stream_pos
= av_gettime() - c
->stream
->prebuffer
* (int64_t)1000;
2171 strcpy(input_filename
, c
->stream
->feed_filename
);
2173 /* compute position (relative time) */
2174 if (av_find_info_tag(buf
, sizeof(buf
), "date", info
)) {
2175 if ((ret
= av_parse_time(&stream_pos
, buf
, 1)) < 0) {
2176 http_log("Invalid date specification '%s' for stream\n", buf
);
2182 if (!input_filename
[0]) {
2183 http_log("No filename was specified for stream\n");
2184 return AVERROR(EINVAL
);
2188 ret
= avformat_open_input(&s
, input_filename
, c
->stream
->ifmt
,
2189 &c
->stream
->in_opts
);
2191 http_log("Could not open input '%s': %s\n",
2192 input_filename
, av_err2str(ret
));
2196 /* set buffer size */
2198 ret
= ffio_set_buf_size(s
->pb
, buf_size
);
2200 http_log("Failed to set buffer size\n");
2205 s
->flags
|= AVFMT_FLAG_GENPTS
;
2207 if (strcmp(s
->iformat
->name
, "ffm") &&
2208 (ret
= avformat_find_stream_info(c
->fmt_in
, NULL
)) < 0) {
2209 http_log("Could not find stream info for input '%s'\n", input_filename
);
2210 avformat_close_input(&s
);
2214 /* choose stream as clock source (we favor the video stream if
2215 * present) for packet sending */
2216 c
->pts_stream_index
= 0;
2217 for(i
=0;i
<c
->stream
->nb_streams
;i
++) {
2218 if (c
->pts_stream_index
== 0 &&
2219 c
->stream
->streams
[i
]->codec
->codec_type
== AVMEDIA_TYPE_VIDEO
) {
2220 c
->pts_stream_index
= i
;
2224 if (c
->fmt_in
->iformat
->read_seek
)
2225 av_seek_frame(c
->fmt_in
, -1, stream_pos
, 0);
2226 /* set the start time (needed for maxtime and RTP packet timing) */
2227 c
->start_time
= cur_time
;
2228 c
->first_pts
= AV_NOPTS_VALUE
;
2232 /* return the server clock (in us) */
2233 static int64_t get_server_clock(HTTPContext
*c
)
2235 /* compute current pts value from system time */
2236 return (cur_time
- c
->start_time
) * 1000;
2239 /* return the estimated time (in us) at which the current packet must be sent */
2240 static int64_t get_packet_send_clock(HTTPContext
*c
)
2242 int bytes_left
, bytes_sent
, frame_bytes
;
2244 frame_bytes
= c
->cur_frame_bytes
;
2245 if (frame_bytes
<= 0)
2248 bytes_left
= c
->buffer_end
- c
->buffer_ptr
;
2249 bytes_sent
= frame_bytes
- bytes_left
;
2250 return c
->cur_pts
+ (c
->cur_frame_duration
* bytes_sent
) / frame_bytes
;
2254 static int http_prepare_data(HTTPContext
*c
)
2257 AVFormatContext
*ctx
;
2259 av_freep(&c
->pb_buffer
);
2261 case HTTPSTATE_SEND_DATA_HEADER
:
2262 ctx
= avformat_alloc_context();
2264 return AVERROR(ENOMEM
);
2267 av_dict_copy(&(c
->fmt_ctx
.metadata
), c
->stream
->metadata
, 0);
2268 c
->fmt_ctx
.streams
= av_mallocz_array(c
->stream
->nb_streams
,
2269 sizeof(AVStream
*));
2270 if (!c
->fmt_ctx
.streams
)
2271 return AVERROR(ENOMEM
);
2273 for(i
=0;i
<c
->stream
->nb_streams
;i
++) {
2275 c
->fmt_ctx
.streams
[i
] = av_mallocz(sizeof(AVStream
));
2277 /* if file or feed, then just take streams from FFServerStream
2279 if (!c
->stream
->feed
||
2280 c
->stream
->feed
== c
->stream
)
2281 src
= c
->stream
->streams
[i
];
2283 src
= c
->stream
->feed
->streams
[c
->stream
->feed_streams
[i
]];
2285 *(c
->fmt_ctx
.streams
[i
]) = *src
;
2286 c
->fmt_ctx
.streams
[i
]->priv_data
= 0;
2287 /* XXX: should be done in AVStream, not in codec */
2288 c
->fmt_ctx
.streams
[i
]->codec
->frame_number
= 0;
2290 /* set output format parameters */
2291 c
->fmt_ctx
.oformat
= c
->stream
->fmt
;
2292 c
->fmt_ctx
.nb_streams
= c
->stream
->nb_streams
;
2294 c
->got_key_frame
= 0;
2296 /* prepare header and save header data in a stream */
2297 if (avio_open_dyn_buf(&c
->fmt_ctx
.pb
) < 0) {
2298 /* XXX: potential leak */
2301 c
->fmt_ctx
.pb
->seekable
= 0;
2304 * HACK to avoid MPEG-PS muxer to spit many underflow errors
2305 * Default value from FFmpeg
2306 * Try to set it using configuration option
2308 c
->fmt_ctx
.max_delay
= (int)(0.7*AV_TIME_BASE
);
2310 if ((ret
= avformat_write_header(&c
->fmt_ctx
, NULL
)) < 0) {
2311 http_log("Error writing output header for stream '%s': %s\n",
2312 c
->stream
->filename
, av_err2str(ret
));
2315 av_dict_free(&c
->fmt_ctx
.metadata
);
2317 len
= avio_close_dyn_buf(c
->fmt_ctx
.pb
, &c
->pb_buffer
);
2318 c
->buffer_ptr
= c
->pb_buffer
;
2319 c
->buffer_end
= c
->pb_buffer
+ len
;
2321 c
->state
= HTTPSTATE_SEND_DATA
;
2322 c
->last_packet_sent
= 0;
2324 case HTTPSTATE_SEND_DATA
:
2325 /* find a new packet */
2326 /* read a packet from the input stream */
2327 if (c
->stream
->feed
)
2328 ffm_set_write_index(c
->fmt_in
,
2329 c
->stream
->feed
->feed_write_index
,
2330 c
->stream
->feed
->feed_size
);
2332 if (c
->stream
->max_time
&&
2333 c
->stream
->max_time
+ c
->start_time
- cur_time
< 0)
2334 /* We have timed out */
2335 c
->state
= HTTPSTATE_SEND_DATA_TRAILER
;
2339 ret
= av_read_frame(c
->fmt_in
, &pkt
);
2341 if (c
->stream
->feed
) {
2342 /* if coming from feed, it means we reached the end of the
2343 * ffm file, so must wait for more data */
2344 c
->state
= HTTPSTATE_WAIT_FEED
;
2345 return 1; /* state changed */
2347 if (ret
== AVERROR(EAGAIN
)) {
2348 /* input not ready, come back later */
2351 if (c
->stream
->loop
) {
2352 avformat_close_input(&c
->fmt_in
);
2353 if (open_input_stream(c
, "") < 0)
2358 /* must send trailer now because EOF or error */
2359 c
->state
= HTTPSTATE_SEND_DATA_TRAILER
;
2362 int source_index
= pkt
.stream_index
;
2363 /* update first pts if needed */
2364 if (c
->first_pts
== AV_NOPTS_VALUE
&& pkt
.dts
!= AV_NOPTS_VALUE
) {
2365 c
->first_pts
= av_rescale_q(pkt
.dts
, c
->fmt_in
->streams
[pkt
.stream_index
]->time_base
, AV_TIME_BASE_Q
);
2366 c
->start_time
= cur_time
;
2368 /* send it to the appropriate stream */
2369 if (c
->stream
->feed
) {
2370 /* if coming from a feed, select the right stream */
2371 if (c
->switch_pending
) {
2372 c
->switch_pending
= 0;
2373 for(i
=0;i
<c
->stream
->nb_streams
;i
++) {
2374 if (c
->switch_feed_streams
[i
] == pkt
.stream_index
)
2375 if (pkt
.flags
& AV_PKT_FLAG_KEY
)
2376 c
->switch_feed_streams
[i
] = -1;
2377 if (c
->switch_feed_streams
[i
] >= 0)
2378 c
->switch_pending
= 1;
2381 for(i
=0;i
<c
->stream
->nb_streams
;i
++) {
2382 if (c
->stream
->feed_streams
[i
] == pkt
.stream_index
) {
2383 AVStream
*st
= c
->fmt_in
->streams
[source_index
];
2384 pkt
.stream_index
= i
;
2385 if (pkt
.flags
& AV_PKT_FLAG_KEY
&&
2386 (st
->codec
->codec_type
== AVMEDIA_TYPE_VIDEO
||
2387 c
->stream
->nb_streams
== 1))
2388 c
->got_key_frame
= 1;
2389 if (!c
->stream
->send_on_key
|| c
->got_key_frame
)
2394 AVCodecContext
*codec
;
2395 AVStream
*ist
, *ost
;
2397 ist
= c
->fmt_in
->streams
[source_index
];
2398 /* specific handling for RTP: we use several
2399 * output streams (one for each RTP connection).
2400 * XXX: need more abstract handling */
2401 if (c
->is_packetized
) {
2402 /* compute send time and duration */
2403 if (pkt
.dts
!= AV_NOPTS_VALUE
) {
2404 c
->cur_pts
= av_rescale_q(pkt
.dts
, ist
->time_base
, AV_TIME_BASE_Q
);
2405 c
->cur_pts
-= c
->first_pts
;
2407 c
->cur_frame_duration
= av_rescale_q(pkt
.duration
, ist
->time_base
, AV_TIME_BASE_Q
);
2408 /* find RTP context */
2409 c
->packet_stream_index
= pkt
.stream_index
;
2410 ctx
= c
->rtp_ctx
[c
->packet_stream_index
];
2412 av_packet_unref(&pkt
);
2415 codec
= ctx
->streams
[0]->codec
;
2416 /* only one stream per RTP connection */
2417 pkt
.stream_index
= 0;
2421 codec
= ctx
->streams
[pkt
.stream_index
]->codec
;
2424 if (c
->is_packetized
) {
2425 int max_packet_size
;
2426 if (c
->rtp_protocol
== RTSP_LOWER_TRANSPORT_TCP
)
2427 max_packet_size
= RTSP_TCP_MAX_PACKET_SIZE
;
2429 max_packet_size
= c
->rtp_handles
[c
->packet_stream_index
]->max_packet_size
;
2430 ret
= ffio_open_dyn_packet_buf(&ctx
->pb
,
2433 ret
= avio_open_dyn_buf(&ctx
->pb
);
2436 /* XXX: potential leak */
2439 ost
= ctx
->streams
[pkt
.stream_index
];
2441 ctx
->pb
->seekable
= 0;
2442 if (pkt
.dts
!= AV_NOPTS_VALUE
)
2443 pkt
.dts
= av_rescale_q(pkt
.dts
, ist
->time_base
,
2445 if (pkt
.pts
!= AV_NOPTS_VALUE
)
2446 pkt
.pts
= av_rescale_q(pkt
.pts
, ist
->time_base
,
2448 pkt
.duration
= av_rescale_q(pkt
.duration
, ist
->time_base
,
2450 if ((ret
= av_write_frame(ctx
, &pkt
)) < 0) {
2451 http_log("Error writing frame to output for stream '%s': %s\n",
2452 c
->stream
->filename
, av_err2str(ret
));
2453 c
->state
= HTTPSTATE_SEND_DATA_TRAILER
;
2456 av_freep(&c
->pb_buffer
);
2457 len
= avio_close_dyn_buf(ctx
->pb
, &c
->pb_buffer
);
2459 c
->cur_frame_bytes
= len
;
2460 c
->buffer_ptr
= c
->pb_buffer
;
2461 c
->buffer_end
= c
->pb_buffer
+ len
;
2463 codec
->frame_number
++;
2465 av_packet_unref(&pkt
);
2469 av_packet_unref(&pkt
);
2474 case HTTPSTATE_SEND_DATA_TRAILER
:
2475 /* last packet test ? */
2476 if (c
->last_packet_sent
|| c
->is_packetized
)
2479 /* prepare header */
2480 if (avio_open_dyn_buf(&ctx
->pb
) < 0) {
2481 /* XXX: potential leak */
2484 c
->fmt_ctx
.pb
->seekable
= 0;
2485 av_write_trailer(ctx
);
2486 len
= avio_close_dyn_buf(ctx
->pb
, &c
->pb_buffer
);
2487 c
->buffer_ptr
= c
->pb_buffer
;
2488 c
->buffer_end
= c
->pb_buffer
+ len
;
2490 c
->last_packet_sent
= 1;
2496 /* should convert the format at the same time */
2497 /* send data starting at c->buffer_ptr to the output connection
2498 * (either UDP or TCP)
2500 static int http_send_data(HTTPContext
*c
)
2505 if (c
->buffer_ptr
>= c
->buffer_end
) {
2506 ret
= http_prepare_data(c
);
2510 /* state change requested */
2513 if (c
->is_packetized
) {
2514 /* RTP data output */
2515 len
= c
->buffer_end
- c
->buffer_ptr
;
2517 /* fail safe - should never happen */
2519 c
->buffer_ptr
= c
->buffer_end
;
2522 len
= (c
->buffer_ptr
[0] << 24) |
2523 (c
->buffer_ptr
[1] << 16) |
2524 (c
->buffer_ptr
[2] << 8) |
2526 if (len
> (c
->buffer_end
- c
->buffer_ptr
))
2528 if ((get_packet_send_clock(c
) - get_server_clock(c
)) > 0) {
2529 /* nothing to send yet: we can wait */
2533 c
->data_count
+= len
;
2534 update_datarate(&c
->datarate
, c
->data_count
);
2536 c
->stream
->bytes_served
+= len
;
2538 if (c
->rtp_protocol
== RTSP_LOWER_TRANSPORT_TCP
) {
2539 /* RTP packets are sent inside the RTSP TCP connection */
2541 int interleaved_index
, size
;
2543 HTTPContext
*rtsp_c
;
2546 /* if no RTSP connection left, error */
2549 /* if already sending something, then wait. */
2550 if (rtsp_c
->state
!= RTSPSTATE_WAIT_REQUEST
)
2552 if (avio_open_dyn_buf(&pb
) < 0)
2554 interleaved_index
= c
->packet_stream_index
* 2;
2555 /* RTCP packets are sent at odd indexes */
2556 if (c
->buffer_ptr
[1] == 200)
2557 interleaved_index
++;
2558 /* write RTSP TCP header */
2560 header
[1] = interleaved_index
;
2561 header
[2] = len
>> 8;
2563 avio_write(pb
, header
, 4);
2564 /* write RTP packet data */
2566 avio_write(pb
, c
->buffer_ptr
, len
);
2567 size
= avio_close_dyn_buf(pb
, &c
->packet_buffer
);
2568 /* prepare asynchronous TCP sending */
2569 rtsp_c
->packet_buffer_ptr
= c
->packet_buffer
;
2570 rtsp_c
->packet_buffer_end
= c
->packet_buffer
+ size
;
2571 c
->buffer_ptr
+= len
;
2573 /* send everything we can NOW */
2574 len
= send(rtsp_c
->fd
, rtsp_c
->packet_buffer_ptr
,
2575 rtsp_c
->packet_buffer_end
- rtsp_c
->packet_buffer_ptr
, 0);
2577 rtsp_c
->packet_buffer_ptr
+= len
;
2578 if (rtsp_c
->packet_buffer_ptr
< rtsp_c
->packet_buffer_end
) {
2579 /* if we could not send all the data, we will
2580 * send it later, so a new state is needed to
2581 * "lock" the RTSP TCP connection */
2582 rtsp_c
->state
= RTSPSTATE_SEND_PACKET
;
2585 /* all data has been sent */
2586 av_freep(&c
->packet_buffer
);
2588 /* send RTP packet directly in UDP */
2590 ffurl_write(c
->rtp_handles
[c
->packet_stream_index
],
2591 c
->buffer_ptr
, len
);
2592 c
->buffer_ptr
+= len
;
2593 /* here we continue as we can send several packets
2597 /* TCP data output */
2598 len
= send(c
->fd
, c
->buffer_ptr
,
2599 c
->buffer_end
- c
->buffer_ptr
, 0);
2601 if (ff_neterrno() != AVERROR(EAGAIN
) &&
2602 ff_neterrno() != AVERROR(EINTR
))
2603 /* error : close connection */
2608 c
->buffer_ptr
+= len
;
2610 c
->data_count
+= len
;
2611 update_datarate(&c
->datarate
, c
->data_count
);
2613 c
->stream
->bytes_served
+= len
;
2621 static int http_start_receive_data(HTTPContext
*c
)
2627 if (c
->stream
->feed_opened
) {
2628 http_log("Stream feed '%s' was not opened\n",
2629 c
->stream
->feed_filename
);
2630 return AVERROR(EINVAL
);
2633 /* Don't permit writing to this one */
2634 if (c
->stream
->readonly
) {
2635 http_log("Cannot write to read-only file '%s'\n",
2636 c
->stream
->feed_filename
);
2637 return AVERROR(EINVAL
);
2641 fd
= open(c
->stream
->feed_filename
, O_RDWR
);
2643 ret
= AVERROR(errno
);
2644 http_log("Could not open feed file '%s': %s\n",
2645 c
->stream
->feed_filename
, strerror(errno
));
2650 if (c
->stream
->truncate
) {
2651 /* truncate feed file */
2652 ffm_write_write_index(c
->feed_fd
, FFM_PACKET_SIZE
);
2653 http_log("Truncating feed file '%s'\n", c
->stream
->feed_filename
);
2654 if (ftruncate(c
->feed_fd
, FFM_PACKET_SIZE
) < 0) {
2655 ret
= AVERROR(errno
);
2656 http_log("Error truncating feed file '%s': %s\n",
2657 c
->stream
->feed_filename
, strerror(errno
));
2661 ret64
= ffm_read_write_index(fd
);
2663 http_log("Error reading write index from feed file '%s': %s\n",
2664 c
->stream
->feed_filename
, strerror(errno
));
2667 c
->stream
->feed_write_index
= ret64
;
2670 c
->stream
->feed_write_index
= FFMAX(ffm_read_write_index(fd
),
2672 c
->stream
->feed_size
= lseek(fd
, 0, SEEK_END
);
2673 lseek(fd
, 0, SEEK_SET
);
2675 /* init buffer input */
2676 c
->buffer_ptr
= c
->buffer
;
2677 c
->buffer_end
= c
->buffer
+ FFM_PACKET_SIZE
;
2678 c
->stream
->feed_opened
= 1;
2679 c
->chunked_encoding
= !!av_stristr(c
->buffer
, "Transfer-Encoding: chunked");
2683 static int http_receive_data(HTTPContext
*c
)
2686 int len
, loop_run
= 0;
2688 while (c
->chunked_encoding
&& !c
->chunk_size
&&
2689 c
->buffer_end
> c
->buffer_ptr
) {
2690 /* read chunk header, if present */
2691 len
= recv(c
->fd
, c
->buffer_ptr
, 1, 0);
2694 if (ff_neterrno() != AVERROR(EAGAIN
) &&
2695 ff_neterrno() != AVERROR(EINTR
))
2696 /* error : close connection */
2699 } else if (len
== 0) {
2700 /* end of connection : close it */
2702 } else if (c
->buffer_ptr
- c
->buffer
>= 2 &&
2703 !memcmp(c
->buffer_ptr
- 1, "\r\n", 2)) {
2704 c
->chunk_size
= strtol(c
->buffer
, 0, 16);
2705 if (c
->chunk_size
<= 0) { // end of stream or invalid chunk size
2709 c
->buffer_ptr
= c
->buffer
;
2711 } else if (++loop_run
> 10)
2712 /* no chunk header, abort */
2718 if (c
->buffer_end
> c
->buffer_ptr
) {
2719 len
= recv(c
->fd
, c
->buffer_ptr
,
2720 FFMIN(c
->chunk_size
, c
->buffer_end
- c
->buffer_ptr
), 0);
2722 if (ff_neterrno() != AVERROR(EAGAIN
) &&
2723 ff_neterrno() != AVERROR(EINTR
))
2724 /* error : close connection */
2726 } else if (len
== 0)
2727 /* end of connection : close it */
2730 av_assert0(len
<= c
->chunk_size
);
2731 c
->chunk_size
-= len
;
2732 c
->buffer_ptr
+= len
;
2733 c
->data_count
+= len
;
2734 update_datarate(&c
->datarate
, c
->data_count
);
2738 if (c
->buffer_ptr
- c
->buffer
>= 2 && c
->data_count
> FFM_PACKET_SIZE
) {
2739 if (c
->buffer
[0] != 'f' ||
2740 c
->buffer
[1] != 'm') {
2741 http_log("Feed stream has become desynchronized -- disconnecting\n");
2746 if (c
->buffer_ptr
>= c
->buffer_end
) {
2747 FFServerStream
*feed
= c
->stream
;
2748 /* a packet has been received : write it in the store, except
2750 if (c
->data_count
> FFM_PACKET_SIZE
) {
2751 /* XXX: use llseek or url_seek
2752 * XXX: Should probably fail? */
2753 if (lseek(c
->feed_fd
, feed
->feed_write_index
, SEEK_SET
) == -1)
2754 http_log("Seek to %"PRId64
" failed\n", feed
->feed_write_index
);
2756 if (write(c
->feed_fd
, c
->buffer
, FFM_PACKET_SIZE
) < 0) {
2757 http_log("Error writing to feed file: %s\n", strerror(errno
));
2761 feed
->feed_write_index
+= FFM_PACKET_SIZE
;
2762 /* update file size */
2763 if (feed
->feed_write_index
> c
->stream
->feed_size
)
2764 feed
->feed_size
= feed
->feed_write_index
;
2766 /* handle wrap around if max file size reached */
2767 if (c
->stream
->feed_max_size
&&
2768 feed
->feed_write_index
>= c
->stream
->feed_max_size
)
2769 feed
->feed_write_index
= FFM_PACKET_SIZE
;
2772 if (ffm_write_write_index(c
->feed_fd
, feed
->feed_write_index
) < 0) {
2773 http_log("Error writing index to feed file: %s\n",
2778 /* wake up any waiting connections */
2779 for(c1
= first_http_ctx
; c1
; c1
= c1
->next
) {
2780 if (c1
->state
== HTTPSTATE_WAIT_FEED
&&
2781 c1
->stream
->feed
== c
->stream
->feed
)
2782 c1
->state
= HTTPSTATE_SEND_DATA
;
2785 /* We have a header in our hands that contains useful data */
2786 AVFormatContext
*s
= avformat_alloc_context();
2788 AVInputFormat
*fmt_in
;
2794 /* use feed output format name to find corresponding input format */
2795 fmt_in
= av_find_input_format(feed
->fmt
->name
);
2799 pb
= avio_alloc_context(c
->buffer
, c
->buffer_end
- c
->buffer
,
2800 0, NULL
, NULL
, NULL
, NULL
);
2807 if (avformat_open_input(&s
, c
->stream
->feed_filename
, fmt_in
, NULL
) < 0) {
2812 /* Now we have the actual streams */
2813 if (s
->nb_streams
!= feed
->nb_streams
) {
2814 avformat_close_input(&s
);
2816 http_log("Feed '%s' stream number does not match registered feed\n",
2817 c
->stream
->feed_filename
);
2821 for (i
= 0; i
< s
->nb_streams
; i
++) {
2822 AVStream
*fst
= feed
->streams
[i
];
2823 AVStream
*st
= s
->streams
[i
];
2824 avcodec_copy_context(fst
->codec
, st
->codec
);
2827 avformat_close_input(&s
);
2830 c
->buffer_ptr
= c
->buffer
;
2835 c
->stream
->feed_opened
= 0;
2837 /* wake up any waiting connections to stop waiting for feed */
2838 for(c1
= first_http_ctx
; c1
; c1
= c1
->next
) {
2839 if (c1
->state
== HTTPSTATE_WAIT_FEED
&&
2840 c1
->stream
->feed
== c
->stream
->feed
)
2841 c1
->state
= HTTPSTATE_SEND_DATA_TRAILER
;
2846 /********************************************************************/
2849 static void rtsp_reply_header(HTTPContext
*c
, enum RTSPStatusCode error_number
)
2856 str
= RTSP_STATUS_CODE2STRING(error_number
);
2858 str
= "Unknown Error";
2860 avio_printf(c
->pb
, "RTSP/1.0 %d %s\r\n", error_number
, str
);
2861 avio_printf(c
->pb
, "CSeq: %d\r\n", c
->seq
);
2863 /* output GMT time */
2866 strftime(buf2
, sizeof(buf2
), "%a, %d %b %Y %H:%M:%S", tm
);
2867 avio_printf(c
->pb
, "Date: %s GMT\r\n", buf2
);
2870 static void rtsp_reply_error(HTTPContext
*c
, enum RTSPStatusCode error_number
)
2872 rtsp_reply_header(c
, error_number
);
2873 avio_printf(c
->pb
, "\r\n");
2876 static int rtsp_parse_request(HTTPContext
*c
)
2878 const char *p
, *p1
, *p2
;
2884 RTSPMessageHeader header1
= { 0 }, *header
= &header1
;
2886 c
->buffer_ptr
[0] = '\0';
2889 get_word(cmd
, sizeof(cmd
), &p
);
2890 get_word(url
, sizeof(url
), &p
);
2891 get_word(protocol
, sizeof(protocol
), &p
);
2893 av_strlcpy(c
->method
, cmd
, sizeof(c
->method
));
2894 av_strlcpy(c
->url
, url
, sizeof(c
->url
));
2895 av_strlcpy(c
->protocol
, protocol
, sizeof(c
->protocol
));
2897 if (avio_open_dyn_buf(&c
->pb
) < 0) {
2898 /* XXX: cannot do more */
2899 c
->pb
= NULL
; /* safety */
2903 /* check version name */
2904 if (strcmp(protocol
, "RTSP/1.0")) {
2905 rtsp_reply_error(c
, RTSP_STATUS_VERSION
);
2909 /* parse each header line */
2910 /* skip to next line */
2911 while (*p
!= '\n' && *p
!= '\0')
2915 while (*p
!= '\0') {
2916 p1
= memchr(p
, '\n', (char *)c
->buffer_ptr
- p
);
2920 if (p2
> p
&& p2
[-1] == '\r')
2922 /* skip empty line */
2926 if (len
> sizeof(line
) - 1)
2927 len
= sizeof(line
) - 1;
2928 memcpy(line
, p
, len
);
2930 ff_rtsp_parse_line(NULL
, header
, line
, NULL
, NULL
);
2934 /* handle sequence number */
2935 c
->seq
= header
->seq
;
2937 if (!strcmp(cmd
, "DESCRIBE"))
2938 rtsp_cmd_describe(c
, url
);
2939 else if (!strcmp(cmd
, "OPTIONS"))
2940 rtsp_cmd_options(c
, url
);
2941 else if (!strcmp(cmd
, "SETUP"))
2942 rtsp_cmd_setup(c
, url
, header
);
2943 else if (!strcmp(cmd
, "PLAY"))
2944 rtsp_cmd_play(c
, url
, header
);
2945 else if (!strcmp(cmd
, "PAUSE"))
2946 rtsp_cmd_interrupt(c
, url
, header
, 1);
2947 else if (!strcmp(cmd
, "TEARDOWN"))
2948 rtsp_cmd_interrupt(c
, url
, header
, 0);
2950 rtsp_reply_error(c
, RTSP_STATUS_METHOD
);
2953 len
= avio_close_dyn_buf(c
->pb
, &c
->pb_buffer
);
2954 c
->pb
= NULL
; /* safety */
2956 /* XXX: cannot do more */
2959 c
->buffer_ptr
= c
->pb_buffer
;
2960 c
->buffer_end
= c
->pb_buffer
+ len
;
2961 c
->state
= RTSPSTATE_SEND_REPLY
;
2965 static int prepare_sdp_description(FFServerStream
*stream
, uint8_t **pbuffer
,
2966 struct in_addr my_ip
)
2968 AVFormatContext
*avc
;
2969 AVStream
*avs
= NULL
;
2970 AVOutputFormat
*rtp_format
= av_guess_format("rtp", NULL
, NULL
);
2971 AVDictionaryEntry
*entry
= av_dict_get(stream
->metadata
, "title", NULL
, 0);
2976 avc
= avformat_alloc_context();
2977 if (!avc
|| !rtp_format
)
2980 avc
->oformat
= rtp_format
;
2981 av_dict_set(&avc
->metadata
, "title",
2982 entry
? entry
->value
: "No Title", 0);
2983 avc
->nb_streams
= stream
->nb_streams
;
2984 if (stream
->is_multicast
) {
2985 snprintf(avc
->filename
, 1024, "rtp://%s:%d?multicast=1?ttl=%d",
2986 inet_ntoa(stream
->multicast_ip
),
2987 stream
->multicast_port
, stream
->multicast_ttl
);
2989 snprintf(avc
->filename
, 1024, "rtp://0.0.0.0");
2991 avc
->streams
= av_malloc_array(avc
->nb_streams
, sizeof(*avc
->streams
));
2995 avs
= av_malloc_array(avc
->nb_streams
, sizeof(*avs
));
2999 for(i
= 0; i
< stream
->nb_streams
; i
++) {
3000 avc
->streams
[i
] = &avs
[i
];
3001 avc
->streams
[i
]->codec
= stream
->streams
[i
]->codec
;
3002 avcodec_parameters_from_context(stream
->streams
[i
]->codecpar
, stream
->streams
[i
]->codec
);
3003 avc
->streams
[i
]->codecpar
= stream
->streams
[i
]->codecpar
;
3005 *pbuffer
= av_mallocz(2048);
3008 av_sdp_create(&avc
, 1, *pbuffer
, 2048);
3011 av_freep(&avc
->streams
);
3012 av_dict_free(&avc
->metadata
);
3016 return *pbuffer
? strlen(*pbuffer
) : AVERROR(ENOMEM
);
3019 static void rtsp_cmd_options(HTTPContext
*c
, const char *url
)
3021 /* rtsp_reply_header(c, RTSP_STATUS_OK); */
3022 avio_printf(c
->pb
, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK
, "OK");
3023 avio_printf(c
->pb
, "CSeq: %d\r\n", c
->seq
);
3024 avio_printf(c
->pb
, "Public: %s\r\n",
3025 "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
3026 avio_printf(c
->pb
, "\r\n");
3029 static void rtsp_cmd_describe(HTTPContext
*c
, const char *url
)
3031 FFServerStream
*stream
;
3037 struct sockaddr_in my_addr
;
3039 /* find which URL is asked */
3040 av_url_split(NULL
, 0, NULL
, 0, NULL
, 0, NULL
, path1
, sizeof(path1
), url
);
3045 for(stream
= config
.first_stream
; stream
; stream
= stream
->next
) {
3046 if (!stream
->is_feed
&&
3047 stream
->fmt
&& !strcmp(stream
->fmt
->name
, "rtp") &&
3048 !strcmp(path
, stream
->filename
)) {
3052 /* no stream found */
3053 rtsp_reply_error(c
, RTSP_STATUS_NOT_FOUND
);
3057 /* prepare the media description in SDP format */
3059 /* get the host IP */
3060 len
= sizeof(my_addr
);
3061 getsockname(c
->fd
, (struct sockaddr
*)&my_addr
, &len
);
3062 content_length
= prepare_sdp_description(stream
, &content
,
3064 if (content_length
< 0) {
3065 rtsp_reply_error(c
, RTSP_STATUS_INTERNAL
);
3068 rtsp_reply_header(c
, RTSP_STATUS_OK
);
3069 avio_printf(c
->pb
, "Content-Base: %s/\r\n", url
);
3070 avio_printf(c
->pb
, "Content-Type: application/sdp\r\n");
3071 avio_printf(c
->pb
, "Content-Length: %d\r\n", content_length
);
3072 avio_printf(c
->pb
, "\r\n");
3073 avio_write(c
->pb
, content
, content_length
);
3077 static HTTPContext
*find_rtp_session(const char *session_id
)
3081 if (session_id
[0] == '\0')
3084 for(c
= first_http_ctx
; c
; c
= c
->next
) {
3085 if (!strcmp(c
->session_id
, session_id
))
3091 static RTSPTransportField
*find_transport(RTSPMessageHeader
*h
, enum RTSPLowerTransport lower_transport
)
3093 RTSPTransportField
*th
;
3096 for(i
=0;i
<h
->nb_transports
;i
++) {
3097 th
= &h
->transports
[i
];
3098 if (th
->lower_transport
== lower_transport
)
3104 static void rtsp_cmd_setup(HTTPContext
*c
, const char *url
,
3105 RTSPMessageHeader
*h
)
3107 FFServerStream
*stream
;
3108 int stream_index
, rtp_port
, rtcp_port
;
3113 RTSPTransportField
*th
;
3114 struct sockaddr_in dest_addr
;
3115 RTSPActionServerSetup setup
;
3117 /* find which URL is asked */
3118 av_url_split(NULL
, 0, NULL
, 0, NULL
, 0, NULL
, path1
, sizeof(path1
), url
);
3123 /* now check each stream */
3124 for(stream
= config
.first_stream
; stream
; stream
= stream
->next
) {
3125 if (stream
->is_feed
|| !stream
->fmt
||
3126 strcmp(stream
->fmt
->name
, "rtp")) {
3129 /* accept aggregate filenames only if single stream */
3130 if (!strcmp(path
, stream
->filename
)) {
3131 if (stream
->nb_streams
!= 1) {
3132 rtsp_reply_error(c
, RTSP_STATUS_AGGREGATE
);
3139 for(stream_index
= 0; stream_index
< stream
->nb_streams
;
3141 snprintf(buf
, sizeof(buf
), "%s/streamid=%d",
3142 stream
->filename
, stream_index
);
3143 if (!strcmp(path
, buf
))
3147 /* no stream found */
3148 rtsp_reply_error(c
, RTSP_STATUS_SERVICE
); /* XXX: right error ? */
3152 /* generate session id if needed */
3153 if (h
->session_id
[0] == '\0') {
3154 unsigned random0
= av_lfg_get(&random_state
);
3155 unsigned random1
= av_lfg_get(&random_state
);
3156 snprintf(h
->session_id
, sizeof(h
->session_id
), "%08x%08x",
3160 /* find RTP session, and create it if none found */
3161 rtp_c
= find_rtp_session(h
->session_id
);
3163 /* always prefer UDP */
3164 th
= find_transport(h
, RTSP_LOWER_TRANSPORT_UDP
);
3166 th
= find_transport(h
, RTSP_LOWER_TRANSPORT_TCP
);
3168 rtsp_reply_error(c
, RTSP_STATUS_TRANSPORT
);
3173 rtp_c
= rtp_new_connection(&c
->from_addr
, stream
, h
->session_id
,
3174 th
->lower_transport
);
3176 rtsp_reply_error(c
, RTSP_STATUS_BANDWIDTH
);
3180 /* open input stream */
3181 if (open_input_stream(rtp_c
, "") < 0) {
3182 rtsp_reply_error(c
, RTSP_STATUS_INTERNAL
);
3187 /* test if stream is OK (test needed because several SETUP needs
3188 * to be done for a given file) */
3189 if (rtp_c
->stream
!= stream
) {
3190 rtsp_reply_error(c
, RTSP_STATUS_SERVICE
);
3194 /* test if stream is already set up */
3195 if (rtp_c
->rtp_ctx
[stream_index
]) {
3196 rtsp_reply_error(c
, RTSP_STATUS_STATE
);
3200 /* check transport */
3201 th
= find_transport(h
, rtp_c
->rtp_protocol
);
3202 if (!th
|| (th
->lower_transport
== RTSP_LOWER_TRANSPORT_UDP
&&
3203 th
->client_port_min
<= 0)) {
3204 rtsp_reply_error(c
, RTSP_STATUS_TRANSPORT
);
3208 /* setup default options */
3209 setup
.transport_option
[0] = '\0';
3210 dest_addr
= rtp_c
->from_addr
;
3211 dest_addr
.sin_port
= htons(th
->client_port_min
);
3214 if (rtp_new_av_stream(rtp_c
, stream_index
, &dest_addr
, c
) < 0) {
3215 rtsp_reply_error(c
, RTSP_STATUS_TRANSPORT
);
3219 /* now everything is OK, so we can send the connection parameters */
3220 rtsp_reply_header(c
, RTSP_STATUS_OK
);
3222 avio_printf(c
->pb
, "Session: %s\r\n", rtp_c
->session_id
);
3224 switch(rtp_c
->rtp_protocol
) {
3225 case RTSP_LOWER_TRANSPORT_UDP
:
3226 rtp_port
= ff_rtp_get_local_rtp_port(rtp_c
->rtp_handles
[stream_index
]);
3227 rtcp_port
= ff_rtp_get_local_rtcp_port(rtp_c
->rtp_handles
[stream_index
]);
3228 avio_printf(c
->pb
, "Transport: RTP/AVP/UDP;unicast;"
3229 "client_port=%d-%d;server_port=%d-%d",
3230 th
->client_port_min
, th
->client_port_max
,
3231 rtp_port
, rtcp_port
);
3233 case RTSP_LOWER_TRANSPORT_TCP
:
3234 avio_printf(c
->pb
, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
3235 stream_index
* 2, stream_index
* 2 + 1);
3240 if (setup
.transport_option
[0] != '\0')
3241 avio_printf(c
->pb
, ";%s", setup
.transport_option
);
3242 avio_printf(c
->pb
, "\r\n");
3245 avio_printf(c
->pb
, "\r\n");
3250 * find an RTP connection by using the session ID. Check consistency
3253 static HTTPContext
*find_rtp_session_with_url(const char *url
,
3254 const char *session_id
)
3262 rtp_c
= find_rtp_session(session_id
);
3266 /* find which URL is asked */
3267 av_url_split(NULL
, 0, NULL
, 0, NULL
, 0, NULL
, path1
, sizeof(path1
), url
);
3271 if(!strcmp(path
, rtp_c
->stream
->filename
)) return rtp_c
;
3272 for(s
=0; s
<rtp_c
->stream
->nb_streams
; ++s
) {
3273 snprintf(buf
, sizeof(buf
), "%s/streamid=%d",
3274 rtp_c
->stream
->filename
, s
);
3275 if(!strncmp(path
, buf
, sizeof(buf
)))
3276 /* XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE
3277 * if nb_streams>1? */
3281 if (len
> 0 && path
[len
- 1] == '/' &&
3282 !strncmp(path
, rtp_c
->stream
->filename
, len
- 1))
3287 static void rtsp_cmd_play(HTTPContext
*c
, const char *url
, RTSPMessageHeader
*h
)
3291 rtp_c
= find_rtp_session_with_url(url
, h
->session_id
);
3293 rtsp_reply_error(c
, RTSP_STATUS_SESSION
);
3297 if (rtp_c
->state
!= HTTPSTATE_SEND_DATA
&&
3298 rtp_c
->state
!= HTTPSTATE_WAIT_FEED
&&
3299 rtp_c
->state
!= HTTPSTATE_READY
) {
3300 rtsp_reply_error(c
, RTSP_STATUS_STATE
);
3304 rtp_c
->state
= HTTPSTATE_SEND_DATA
;
3306 /* now everything is OK, so we can send the connection parameters */
3307 rtsp_reply_header(c
, RTSP_STATUS_OK
);
3309 avio_printf(c
->pb
, "Session: %s\r\n", rtp_c
->session_id
);
3310 avio_printf(c
->pb
, "\r\n");
3313 static void rtsp_cmd_interrupt(HTTPContext
*c
, const char *url
,
3314 RTSPMessageHeader
*h
, int pause_only
)
3318 rtp_c
= find_rtp_session_with_url(url
, h
->session_id
);
3320 rtsp_reply_error(c
, RTSP_STATUS_SESSION
);
3325 if (rtp_c
->state
!= HTTPSTATE_SEND_DATA
&&
3326 rtp_c
->state
!= HTTPSTATE_WAIT_FEED
) {
3327 rtsp_reply_error(c
, RTSP_STATUS_STATE
);
3330 rtp_c
->state
= HTTPSTATE_READY
;
3331 rtp_c
->first_pts
= AV_NOPTS_VALUE
;
3334 /* now everything is OK, so we can send the connection parameters */
3335 rtsp_reply_header(c
, RTSP_STATUS_OK
);
3337 avio_printf(c
->pb
, "Session: %s\r\n", rtp_c
->session_id
);
3338 avio_printf(c
->pb
, "\r\n");
3341 close_connection(rtp_c
);
3344 /********************************************************************/
3347 static HTTPContext
*rtp_new_connection(struct sockaddr_in
*from_addr
,
3348 FFServerStream
*stream
,
3349 const char *session_id
,
3350 enum RTSPLowerTransport rtp_protocol
)
3352 HTTPContext
*c
= NULL
;
3353 const char *proto_str
;
3355 /* XXX: should output a warning page when coming
3356 * close to the connection limit */
3357 if (nb_connections
>= config
.nb_max_connections
)
3360 /* add a new connection */
3361 c
= av_mallocz(sizeof(HTTPContext
));
3366 c
->poll_entry
= NULL
;
3367 c
->from_addr
= *from_addr
;
3368 c
->buffer_size
= IOBUFFER_INIT_SIZE
;
3369 c
->buffer
= av_malloc(c
->buffer_size
);
3374 av_strlcpy(c
->session_id
, session_id
, sizeof(c
->session_id
));
3375 c
->state
= HTTPSTATE_READY
;
3376 c
->is_packetized
= 1;
3377 c
->rtp_protocol
= rtp_protocol
;
3379 /* protocol is shown in statistics */
3380 switch(c
->rtp_protocol
) {
3381 case RTSP_LOWER_TRANSPORT_UDP_MULTICAST
:
3382 proto_str
= "MCAST";
3384 case RTSP_LOWER_TRANSPORT_UDP
:
3387 case RTSP_LOWER_TRANSPORT_TCP
:
3394 av_strlcpy(c
->protocol
, "RTP/", sizeof(c
->protocol
));
3395 av_strlcat(c
->protocol
, proto_str
, sizeof(c
->protocol
));
3397 current_bandwidth
+= stream
->bandwidth
;
3399 c
->next
= first_http_ctx
;
3405 av_freep(&c
->buffer
);
3412 * add a new RTP stream in an RTP connection (used in RTSP SETUP
3413 * command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
3416 static int rtp_new_av_stream(HTTPContext
*c
,
3417 int stream_index
, struct sockaddr_in
*dest_addr
,
3418 HTTPContext
*rtsp_c
)
3420 AVFormatContext
*ctx
;
3423 URLContext
*h
= NULL
;
3425 int max_packet_size
;
3428 /* now we can open the relevant output stream */
3429 ctx
= avformat_alloc_context();
3432 ctx
->oformat
= av_guess_format("rtp", NULL
, NULL
);
3434 st
= avformat_new_stream(ctx
, NULL
);
3438 av_freep(&st
->codec
);
3439 av_freep(&st
->info
);
3440 st_internal
= st
->internal
;
3442 if (!c
->stream
->feed
||
3443 c
->stream
->feed
== c
->stream
)
3444 memcpy(st
, c
->stream
->streams
[stream_index
], sizeof(AVStream
));
3447 c
->stream
->feed
->streams
[c
->stream
->feed_streams
[stream_index
]],
3449 st
->priv_data
= NULL
;
3450 st
->internal
= st_internal
;
3452 /* build destination RTP address */
3453 ipaddr
= inet_ntoa(dest_addr
->sin_addr
);
3455 switch(c
->rtp_protocol
) {
3456 case RTSP_LOWER_TRANSPORT_UDP
:
3457 case RTSP_LOWER_TRANSPORT_UDP_MULTICAST
:
3460 /* XXX: also pass as parameter to function ? */
3461 if (c
->stream
->is_multicast
) {
3463 ttl
= c
->stream
->multicast_ttl
;
3466 snprintf(ctx
->filename
, sizeof(ctx
->filename
),
3467 "rtp://%s:%d?multicast=1&ttl=%d",
3468 ipaddr
, ntohs(dest_addr
->sin_port
), ttl
);
3470 snprintf(ctx
->filename
, sizeof(ctx
->filename
),
3471 "rtp://%s:%d", ipaddr
, ntohs(dest_addr
->sin_port
));
3474 if (ffurl_open(&h
, ctx
->filename
, AVIO_FLAG_WRITE
, NULL
, NULL
) < 0)
3476 c
->rtp_handles
[stream_index
] = h
;
3477 max_packet_size
= h
->max_packet_size
;
3479 case RTSP_LOWER_TRANSPORT_TCP
:
3482 max_packet_size
= RTSP_TCP_MAX_PACKET_SIZE
;
3488 http_log("%s:%d - - \"PLAY %s/streamid=%d %s\"\n",
3489 ipaddr
, ntohs(dest_addr
->sin_port
),
3490 c
->stream
->filename
, stream_index
, c
->protocol
);
3492 /* normally, no packets should be output here, but the packet size may
3494 if (ffio_open_dyn_packet_buf(&ctx
->pb
, max_packet_size
) < 0)
3495 /* XXX: close stream */
3498 if (avformat_write_header(ctx
, NULL
) < 0) {
3506 avio_close_dyn_buf(ctx
->pb
, &dummy_buf
);
3510 c
->rtp_ctx
[stream_index
] = ctx
;
3514 /********************************************************************/
3515 /* ffserver initialization */
3517 /* FIXME: This code should use avformat_new_stream() */
3518 static AVStream
*add_av_stream1(FFServerStream
*stream
,
3519 AVCodecContext
*codec
, int copy
)
3523 if(stream
->nb_streams
>= FF_ARRAY_ELEMS(stream
->streams
))
3526 fst
= av_mallocz(sizeof(AVStream
));
3530 fst
->codec
= avcodec_alloc_context3(codec
->codec
);
3535 avcodec_copy_context(fst
->codec
, codec
);
3537 /* live streams must use the actual feed's codec since it may be
3538 * updated later to carry extradata needed by them.
3542 fst
->priv_data
= av_mallocz(sizeof(FeedData
));
3543 fst
->internal
= av_mallocz(sizeof(*fst
->internal
));
3544 fst
->internal
->avctx
= avcodec_alloc_context3(NULL
);
3545 fst
->codecpar
= avcodec_parameters_alloc();
3546 fst
->index
= stream
->nb_streams
;
3547 avpriv_set_pts_info(fst
, 33, 1, 90000);
3548 fst
->sample_aspect_ratio
= codec
->sample_aspect_ratio
;
3549 stream
->streams
[stream
->nb_streams
++] = fst
;
3553 /* return the stream number in the feed */
3554 static int add_av_stream(FFServerStream
*feed
, AVStream
*st
)
3557 AVCodecContext
*av
, *av1
;
3561 for(i
=0;i
<feed
->nb_streams
;i
++) {
3562 av1
= feed
->streams
[i
]->codec
;
3563 if (av1
->codec_id
== av
->codec_id
&&
3564 av1
->codec_type
== av
->codec_type
&&
3565 av1
->bit_rate
== av
->bit_rate
) {
3567 switch(av
->codec_type
) {
3568 case AVMEDIA_TYPE_AUDIO
:
3569 if (av1
->channels
== av
->channels
&&
3570 av1
->sample_rate
== av
->sample_rate
)
3573 case AVMEDIA_TYPE_VIDEO
:
3574 if (av1
->width
== av
->width
&&
3575 av1
->height
== av
->height
&&
3576 av1
->time_base
.den
== av
->time_base
.den
&&
3577 av1
->time_base
.num
== av
->time_base
.num
&&
3578 av1
->gop_size
== av
->gop_size
)
3587 fst
= add_av_stream1(feed
, av
, 0);
3590 if (av_stream_get_recommended_encoder_configuration(st
))
3591 av_stream_set_recommended_encoder_configuration(fst
,
3592 av_strdup(av_stream_get_recommended_encoder_configuration(st
)));
3593 return feed
->nb_streams
- 1;
3596 static void remove_stream(FFServerStream
*stream
)
3598 FFServerStream
**ps
;
3599 ps
= &config
.first_stream
;
3608 /* specific MPEG4 handling : we extract the raw parameters */
3609 static void extract_mpeg4_header(AVFormatContext
*infile
)
3611 int mpeg4_count
, i
, size
;
3616 infile
->flags
|= AVFMT_FLAG_NOFILLIN
| AVFMT_FLAG_NOPARSE
;
3619 for(i
=0;i
<infile
->nb_streams
;i
++) {
3620 st
= infile
->streams
[i
];
3621 if (st
->codec
->codec_id
== AV_CODEC_ID_MPEG4
&&
3622 st
->codec
->extradata_size
== 0) {
3629 printf("MPEG4 without extra data: trying to find header in %s\n",
3631 while (mpeg4_count
> 0) {
3632 if (av_read_frame(infile
, &pkt
) < 0)
3634 st
= infile
->streams
[pkt
.stream_index
];
3635 if (st
->codec
->codec_id
== AV_CODEC_ID_MPEG4
&&
3636 st
->codec
->extradata_size
== 0) {
3637 av_freep(&st
->codec
->extradata
);
3638 /* fill extradata with the header */
3639 /* XXX: we make hard suppositions here ! */
3641 while (p
< pkt
.data
+ pkt
.size
- 4) {
3642 /* stop when vop header is found */
3643 if (p
[0] == 0x00 && p
[1] == 0x00 &&
3644 p
[2] == 0x01 && p
[3] == 0xb6) {
3645 size
= p
- pkt
.data
;
3646 st
->codec
->extradata
= av_mallocz(size
+ AV_INPUT_BUFFER_PADDING_SIZE
);
3647 st
->codec
->extradata_size
= size
;
3648 memcpy(st
->codec
->extradata
, pkt
.data
, size
);
3655 av_packet_unref(&pkt
);
3659 /* compute the needed AVStream for each file */
3660 static void build_file_streams(void)
3662 FFServerStream
*stream
;
3663 AVFormatContext
*infile
;
3666 /* gather all streams */
3667 for(stream
= config
.first_stream
; stream
; stream
= stream
->next
) {
3670 if (stream
->stream_type
!= STREAM_TYPE_LIVE
|| stream
->feed
)
3673 /* the stream comes from a file */
3674 /* try to open the file */
3678 /* specific case: if transport stream output to RTP,
3679 * we use a raw transport stream reader */
3680 if (stream
->fmt
&& !strcmp(stream
->fmt
->name
, "rtp"))
3681 av_dict_set(&stream
->in_opts
, "mpeg2ts_compute_pcr", "1", 0);
3683 if (!stream
->feed_filename
[0]) {
3684 http_log("Unspecified feed file for stream '%s'\n",
3689 http_log("Opening feed file '%s' for stream '%s'\n",
3690 stream
->feed_filename
, stream
->filename
);
3692 ret
= avformat_open_input(&infile
, stream
->feed_filename
,
3693 stream
->ifmt
, &stream
->in_opts
);
3695 http_log("Could not open '%s': %s\n", stream
->feed_filename
,
3697 /* remove stream (no need to spend more time on it) */
3699 remove_stream(stream
);
3701 /* find all the AVStreams inside and reference them in
3703 if (avformat_find_stream_info(infile
, NULL
) < 0) {
3704 http_log("Could not find codec parameters from '%s'\n",
3705 stream
->feed_filename
);
3706 avformat_close_input(&infile
);
3709 extract_mpeg4_header(infile
);
3711 for(i
=0;i
<infile
->nb_streams
;i
++)
3712 add_av_stream1(stream
, infile
->streams
[i
]->codec
, 1);
3714 avformat_close_input(&infile
);
3720 int check_codec_match(AVCodecContext
*ccf
, AVCodecContext
*ccs
, int stream
)
3724 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3725 if (CHECK_CODEC(codec_id
) || CHECK_CODEC(codec_type
)) {
3726 http_log("Codecs do not match for stream %d\n", stream
);
3728 } else if (CHECK_CODEC(bit_rate
) || CHECK_CODEC(flags
)) {
3729 http_log("Codec bitrates do not match for stream %d\n", stream
);
3731 } else if (ccf
->codec_type
== AVMEDIA_TYPE_VIDEO
) {
3732 if (CHECK_CODEC(time_base
.den
) ||
3733 CHECK_CODEC(time_base
.num
) ||
3734 CHECK_CODEC(width
) ||
3735 CHECK_CODEC(height
)) {
3736 http_log("Codec width, height or framerate do not match for stream %d\n", stream
);
3739 } else if (ccf
->codec_type
== AVMEDIA_TYPE_AUDIO
) {
3740 if (CHECK_CODEC(sample_rate
) ||
3741 CHECK_CODEC(channels
) ||
3742 CHECK_CODEC(frame_size
)) {
3743 http_log("Codec sample_rate, channels, frame_size do not match for stream %d\n", stream
);
3747 http_log("Unknown codec type for stream %d\n", stream
);
3754 /* compute the needed AVStream for each feed */
3755 static int build_feed_streams(void)
3757 FFServerStream
*stream
, *feed
;
3760 /* gather all streams */
3761 for(stream
= config
.first_stream
; stream
; stream
= stream
->next
) {
3762 feed
= stream
->feed
;
3766 if (stream
->is_feed
) {
3767 for(i
=0;i
<stream
->nb_streams
;i
++)
3768 stream
->feed_streams
[i
] = i
;
3771 /* we handle a stream coming from a feed */
3772 for(i
=0;i
<stream
->nb_streams
;i
++)
3773 stream
->feed_streams
[i
] = add_av_stream(feed
, stream
->streams
[i
]);
3776 /* create feed files if needed */
3777 for(feed
= config
.first_feed
; feed
; feed
= feed
->next_feed
) {
3779 if (avio_check(feed
->feed_filename
, AVIO_FLAG_READ
) > 0) {
3780 AVFormatContext
*s
= NULL
;
3783 /* See if it matches */
3785 if (avformat_open_input(&s
, feed
->feed_filename
, NULL
, NULL
) < 0) {
3786 http_log("Deleting feed file '%s' as it appears "
3788 feed
->feed_filename
);
3792 /* set buffer size */
3793 if (ffio_set_buf_size(s
->pb
, FFM_PACKET_SIZE
) < 0) {
3794 http_log("Failed to set buffer size\n");
3795 avformat_close_input(&s
);
3799 /* Now see if it matches */
3800 if (s
->nb_streams
!= feed
->nb_streams
) {
3801 http_log("Deleting feed file '%s' as stream counts "
3802 "differ (%d != %d)\n",
3803 feed
->feed_filename
, s
->nb_streams
, feed
->nb_streams
);
3808 for(i
=0;i
<s
->nb_streams
;i
++) {
3811 sf
= feed
->streams
[i
];
3814 if (sf
->index
!= ss
->index
|| sf
->id
!= ss
->id
) {
3815 http_log("Index & Id do not match for stream %d (%s)\n",
3816 i
, feed
->feed_filename
);
3821 matches
= check_codec_match (sf
->codec
, ss
->codec
, i
);
3828 avformat_close_input(&s
);
3831 if (feed
->readonly
) {
3832 http_log("Unable to delete read-only feed file '%s'\n",
3833 feed
->feed_filename
);
3836 unlink(feed
->feed_filename
);
3840 if (avio_check(feed
->feed_filename
, AVIO_FLAG_WRITE
) <= 0) {
3841 AVFormatContext
*s
= avformat_alloc_context();
3844 http_log("Failed to allocate context\n");
3848 if (feed
->readonly
) {
3849 http_log("Unable to create feed file '%s' as it is "
3850 "marked readonly\n",
3851 feed
->feed_filename
);
3852 avformat_free_context(s
);
3856 /* only write the header of the ffm file */
3857 if (avio_open(&s
->pb
, feed
->feed_filename
, AVIO_FLAG_WRITE
) < 0) {
3858 http_log("Could not open output feed file '%s'\n",
3859 feed
->feed_filename
);
3860 avformat_free_context(s
);
3863 s
->oformat
= feed
->fmt
;
3864 s
->nb_streams
= feed
->nb_streams
;
3865 s
->streams
= feed
->streams
;
3866 if (avformat_write_header(s
, NULL
) < 0) {
3867 http_log("Container doesn't support the required parameters\n");
3868 avio_closep(&s
->pb
);
3871 avformat_free_context(s
);
3874 /* XXX: need better API */
3875 av_freep(&s
->priv_data
);
3876 avio_closep(&s
->pb
);
3879 avformat_free_context(s
);
3882 /* get feed size and write index */
3883 fd
= open(feed
->feed_filename
, O_RDONLY
);
3885 http_log("Could not open output feed file '%s'\n",
3886 feed
->feed_filename
);
3890 feed
->feed_write_index
= FFMAX(ffm_read_write_index(fd
),
3892 feed
->feed_size
= lseek(fd
, 0, SEEK_END
);
3893 /* ensure that we do not wrap before the end of file */
3894 if (feed
->feed_max_size
&& feed
->feed_max_size
< feed
->feed_size
)
3895 feed
->feed_max_size
= feed
->feed_size
;
3905 /* compute the bandwidth used by each stream */
3906 static void compute_bandwidth(void)
3910 FFServerStream
*stream
;
3912 for(stream
= config
.first_stream
; stream
; stream
= stream
->next
) {
3914 for(i
=0;i
<stream
->nb_streams
;i
++) {
3915 AVStream
*st
= stream
->streams
[i
];
3916 switch(st
->codec
->codec_type
) {
3917 case AVMEDIA_TYPE_AUDIO
:
3918 case AVMEDIA_TYPE_VIDEO
:
3919 bandwidth
+= st
->codec
->bit_rate
;
3925 stream
->bandwidth
= (bandwidth
+ 999) / 1000;
3929 static void handle_child_exit(int sig
)
3935 while ((pid
= waitpid(-1, &status
, WNOHANG
)) > 0) {
3936 FFServerStream
*feed
;
3938 for (feed
= config
.first_feed
; feed
; feed
= feed
->next
) {
3939 if (feed
->pid
!= pid
)
3942 uptime
= time(0) - feed
->pid_start
;
3945 "%s: Pid %"PRId64
" exited with status %d after %"PRId64
" "
3947 feed
->filename
, (int64_t) pid
, status
, (int64_t)uptime
);
3950 /* Turn off any more restarts */
3951 ffserver_free_child_args(&feed
->child_argv
);
3955 need_to_start_children
= 1;
3958 static void opt_debug(void)
3961 snprintf(config
.logfilename
, sizeof(config
.logfilename
), "-");
3964 void show_help_default(const char *opt
, const char *arg
)
3966 printf("usage: ffserver [options]\n"
3967 "Hyper fast multi format Audio/Video streaming server\n");
3969 show_help_options(options
, "Main options:", 0, 0, 0);
3972 static const OptionDef options
[] = {
3973 #include "cmdutils_common_opts.h"
3974 { "n", OPT_BOOL
, {(void *)&no_launch
}, "enable no-launch mode" },
3975 { "d", 0, {(void*)opt_debug
}, "enable debug mode" },
3976 { "f", HAS_ARG
| OPT_STRING
, {(void*)&config
.filename
}, "use configfile instead of /etc/ffserver.conf", "configfile" },
3980 int main(int argc
, char **argv
)
3982 struct sigaction sigact
= { { 0 } };
3984 int ret
= EXIT_FAILURE
;
3988 config
.filename
= av_strdup("/etc/ffserver.conf");
3990 parse_loglevel(argc
, argv
, options
);
3992 avformat_network_init();
3994 show_banner(argc
, argv
, options
);
3996 my_program_name
= argv
[0];
3998 parse_options(NULL
, argc
, argv
, options
, NULL
);
4000 unsetenv("http_proxy"); /* Kill the http_proxy */
4002 av_lfg_init(&random_state
, av_get_random_seed());
4004 sigact
.sa_handler
= handle_child_exit
;
4005 sigact
.sa_flags
= SA_NOCLDSTOP
| SA_RESTART
;
4006 sigaction(SIGCHLD
, &sigact
, 0);
4008 if ((cfg_parsed
= ffserver_parse_ffconfig(config
.filename
, &config
)) < 0) {
4009 fprintf(stderr
, "Error reading configuration file '%s': %s\n",
4010 config
.filename
, av_err2str(cfg_parsed
));
4014 /* open log file if needed */
4015 if (config
.logfilename
[0] != '\0') {
4016 if (!strcmp(config
.logfilename
, "-"))
4019 logfile
= fopen(config
.logfilename
, "a");
4020 av_log_set_callback(http_av_log
);
4023 build_file_streams();
4025 if (build_feed_streams() < 0) {
4026 http_log("Could not setup feed streams\n");
4030 compute_bandwidth();
4033 signal(SIGPIPE
, SIG_IGN
);
4035 if (http_server() < 0) {
4036 http_log("Could not start server\n");
4043 av_freep (&config
.filename
);
4044 avformat_network_deinit();