Libav
|
00001 /* 00002 * Multiple format streaming server 00003 * Copyright (c) 2000, 2001, 2002 Fabrice Bellard 00004 * 00005 * This file is part of FFmpeg. 00006 * 00007 * FFmpeg is free software; you can redistribute it and/or 00008 * modify it under the terms of the GNU Lesser General Public 00009 * License as published by the Free Software Foundation; either 00010 * version 2.1 of the License, or (at your option) any later version. 00011 * 00012 * FFmpeg is distributed in the hope that it will be useful, 00013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00015 * Lesser General Public License for more details. 00016 * 00017 * You should have received a copy of the GNU Lesser General Public 00018 * License along with FFmpeg; if not, write to the Free Software 00019 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00020 */ 00021 00022 #define _XOPEN_SOURCE 600 00023 00024 #include "config.h" 00025 #if !HAVE_CLOSESOCKET 00026 #define closesocket close 00027 #endif 00028 #include <string.h> 00029 #include <strings.h> 00030 #include <stdlib.h> 00031 #include "libavformat/avformat.h" 00032 #include "libavformat/network.h" 00033 #include "libavformat/os_support.h" 00034 #include "libavformat/rtpdec.h" 00035 #include "libavformat/rtsp.h" 00036 #include "libavutil/avstring.h" 00037 #include "libavutil/lfg.h" 00038 #include "libavutil/random_seed.h" 00039 #include "libavcodec/opt.h" 00040 #include <stdarg.h> 00041 #include <unistd.h> 00042 #include <fcntl.h> 00043 #include <sys/ioctl.h> 00044 #if HAVE_POLL_H 00045 #include <poll.h> 00046 #endif 00047 #include <errno.h> 00048 #include <sys/time.h> 00049 #include <time.h> 00050 #include <sys/wait.h> 00051 #include <signal.h> 00052 #if HAVE_DLFCN_H 00053 #include <dlfcn.h> 00054 #endif 00055 00056 #include "cmdutils.h" 00057 00058 const char program_name[] = "FFserver"; 00059 const int program_birth_year = 2000; 00060 00061 static const OptionDef options[]; 00062 00063 enum HTTPState { 00064 HTTPSTATE_WAIT_REQUEST, 00065 HTTPSTATE_SEND_HEADER, 00066 HTTPSTATE_SEND_DATA_HEADER, 00067 HTTPSTATE_SEND_DATA, /* sending TCP or UDP data */ 00068 HTTPSTATE_SEND_DATA_TRAILER, 00069 HTTPSTATE_RECEIVE_DATA, 00070 HTTPSTATE_WAIT_FEED, /* wait for data from the feed */ 00071 HTTPSTATE_READY, 00072 00073 RTSPSTATE_WAIT_REQUEST, 00074 RTSPSTATE_SEND_REPLY, 00075 RTSPSTATE_SEND_PACKET, 00076 }; 00077 00078 static const char *http_state[] = { 00079 "HTTP_WAIT_REQUEST", 00080 "HTTP_SEND_HEADER", 00081 00082 "SEND_DATA_HEADER", 00083 "SEND_DATA", 00084 "SEND_DATA_TRAILER", 00085 "RECEIVE_DATA", 00086 "WAIT_FEED", 00087 "READY", 00088 00089 "RTSP_WAIT_REQUEST", 00090 "RTSP_SEND_REPLY", 00091 "RTSP_SEND_PACKET", 00092 }; 00093 00094 #define IOBUFFER_INIT_SIZE 8192 00095 00096 /* timeouts are in ms */ 00097 #define HTTP_REQUEST_TIMEOUT (15 * 1000) 00098 #define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000) 00099 00100 #define SYNC_TIMEOUT (10 * 1000) 00101 00102 typedef struct RTSPActionServerSetup { 00103 uint32_t ipaddr; 00104 char transport_option[512]; 00105 } RTSPActionServerSetup; 00106 00107 typedef struct { 00108 int64_t count1, count2; 00109 int64_t time1, time2; 00110 } DataRateData; 00111 00112 /* context associated with one connection */ 00113 typedef struct HTTPContext { 00114 enum HTTPState state; 00115 int fd; /* socket file descriptor */ 00116 struct sockaddr_in from_addr; /* origin */ 00117 struct pollfd *poll_entry; /* used when polling */ 00118 int64_t timeout; 00119 uint8_t *buffer_ptr, *buffer_end; 00120 int http_error; 00121 int post; 00122 int chunked_encoding; 00123 int chunk_size; /* 0 if it needs to be read */ 00124 struct HTTPContext *next; 00125 int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */ 00126 int64_t data_count; 00127 /* feed input */ 00128 int feed_fd; 00129 /* input format handling */ 00130 AVFormatContext *fmt_in; 00131 int64_t start_time; /* In milliseconds - this wraps fairly often */ 00132 int64_t first_pts; /* initial pts value */ 00133 int64_t cur_pts; /* current pts value from the stream in us */ 00134 int64_t cur_frame_duration; /* duration of the current frame in us */ 00135 int cur_frame_bytes; /* output frame size, needed to compute 00136 the time at which we send each 00137 packet */ 00138 int pts_stream_index; /* stream we choose as clock reference */ 00139 int64_t cur_clock; /* current clock reference value in us */ 00140 /* output format handling */ 00141 struct FFStream *stream; 00142 /* -1 is invalid stream */ 00143 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */ 00144 int switch_feed_streams[MAX_STREAMS]; /* index of streams in the feed */ 00145 int switch_pending; 00146 AVFormatContext fmt_ctx; /* instance of FFStream for one user */ 00147 int last_packet_sent; /* true if last data packet was sent */ 00148 int suppress_log; 00149 DataRateData datarate; 00150 int wmp_client_id; 00151 char protocol[16]; 00152 char method[16]; 00153 char url[128]; 00154 int buffer_size; 00155 uint8_t *buffer; 00156 int is_packetized; /* if true, the stream is packetized */ 00157 int packet_stream_index; /* current stream for output in state machine */ 00158 00159 /* RTSP state specific */ 00160 uint8_t *pb_buffer; /* XXX: use that in all the code */ 00161 ByteIOContext *pb; 00162 int seq; /* RTSP sequence number */ 00163 00164 /* RTP state specific */ 00165 enum RTSPLowerTransport rtp_protocol; 00166 char session_id[32]; /* session id */ 00167 AVFormatContext *rtp_ctx[MAX_STREAMS]; 00168 00169 /* RTP/UDP specific */ 00170 URLContext *rtp_handles[MAX_STREAMS]; 00171 00172 /* RTP/TCP specific */ 00173 struct HTTPContext *rtsp_c; 00174 uint8_t *packet_buffer, *packet_buffer_ptr, *packet_buffer_end; 00175 } HTTPContext; 00176 00177 /* each generated stream is described here */ 00178 enum StreamType { 00179 STREAM_TYPE_LIVE, 00180 STREAM_TYPE_STATUS, 00181 STREAM_TYPE_REDIRECT, 00182 }; 00183 00184 enum IPAddressAction { 00185 IP_ALLOW = 1, 00186 IP_DENY, 00187 }; 00188 00189 typedef struct IPAddressACL { 00190 struct IPAddressACL *next; 00191 enum IPAddressAction action; 00192 /* These are in host order */ 00193 struct in_addr first; 00194 struct in_addr last; 00195 } IPAddressACL; 00196 00197 /* description of each stream of the ffserver.conf file */ 00198 typedef struct FFStream { 00199 enum StreamType stream_type; 00200 char filename[1024]; /* stream filename */ 00201 struct FFStream *feed; /* feed we are using (can be null if 00202 coming from file) */ 00203 AVFormatParameters *ap_in; /* input parameters */ 00204 AVInputFormat *ifmt; /* if non NULL, force input format */ 00205 AVOutputFormat *fmt; 00206 IPAddressACL *acl; 00207 char dynamic_acl[1024]; 00208 int nb_streams; 00209 int prebuffer; /* Number of millseconds early to start */ 00210 int64_t max_time; /* Number of milliseconds to run */ 00211 int send_on_key; 00212 AVStream *streams[MAX_STREAMS]; 00213 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */ 00214 char feed_filename[1024]; /* file name of the feed storage, or 00215 input file name for a stream */ 00216 char author[512]; 00217 char title[512]; 00218 char copyright[512]; 00219 char comment[512]; 00220 pid_t pid; /* Of ffmpeg process */ 00221 time_t pid_start; /* Of ffmpeg process */ 00222 char **child_argv; 00223 struct FFStream *next; 00224 unsigned bandwidth; /* bandwidth, in kbits/s */ 00225 /* RTSP options */ 00226 char *rtsp_option; 00227 /* multicast specific */ 00228 int is_multicast; 00229 struct in_addr multicast_ip; 00230 int multicast_port; /* first port used for multicast */ 00231 int multicast_ttl; 00232 int loop; /* if true, send the stream in loops (only meaningful if file) */ 00233 00234 /* feed specific */ 00235 int feed_opened; /* true if someone is writing to the feed */ 00236 int is_feed; /* true if it is a feed */ 00237 int readonly; /* True if writing is prohibited to the file */ 00238 int truncate; /* True if feeder connection truncate the feed file */ 00239 int conns_served; 00240 int64_t bytes_served; 00241 int64_t feed_max_size; /* maximum storage size, zero means unlimited */ 00242 int64_t feed_write_index; /* current write position in feed (it wraps around) */ 00243 int64_t feed_size; /* current size of feed */ 00244 struct FFStream *next_feed; 00245 } FFStream; 00246 00247 typedef struct FeedData { 00248 long long data_count; 00249 float avg_frame_size; /* frame size averaged over last frames with exponential mean */ 00250 } FeedData; 00251 00252 static struct sockaddr_in my_http_addr; 00253 static struct sockaddr_in my_rtsp_addr; 00254 00255 static char logfilename[1024]; 00256 static HTTPContext *first_http_ctx; 00257 static FFStream *first_feed; /* contains only feeds */ 00258 static FFStream *first_stream; /* contains all streams, including feeds */ 00259 00260 static void new_connection(int server_fd, int is_rtsp); 00261 static void close_connection(HTTPContext *c); 00262 00263 /* HTTP handling */ 00264 static int handle_connection(HTTPContext *c); 00265 static int http_parse_request(HTTPContext *c); 00266 static int http_send_data(HTTPContext *c); 00267 static void compute_status(HTTPContext *c); 00268 static int open_input_stream(HTTPContext *c, const char *info); 00269 static int http_start_receive_data(HTTPContext *c); 00270 static int http_receive_data(HTTPContext *c); 00271 00272 /* RTSP handling */ 00273 static int rtsp_parse_request(HTTPContext *c); 00274 static void rtsp_cmd_describe(HTTPContext *c, const char *url); 00275 static void rtsp_cmd_options(HTTPContext *c, const char *url); 00276 static void rtsp_cmd_setup(HTTPContext *c, const char *url, RTSPMessageHeader *h); 00277 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPMessageHeader *h); 00278 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPMessageHeader *h); 00279 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPMessageHeader *h); 00280 00281 /* SDP handling */ 00282 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer, 00283 struct in_addr my_ip); 00284 00285 /* RTP handling */ 00286 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr, 00287 FFStream *stream, const char *session_id, 00288 enum RTSPLowerTransport rtp_protocol); 00289 static int rtp_new_av_stream(HTTPContext *c, 00290 int stream_index, struct sockaddr_in *dest_addr, 00291 HTTPContext *rtsp_c); 00292 00293 static const char *my_program_name; 00294 static const char *my_program_dir; 00295 00296 static const char *config_filename = "/etc/ffserver.conf"; 00297 00298 static int ffserver_debug; 00299 static int ffserver_daemon; 00300 static int no_launch; 00301 static int need_to_start_children; 00302 00303 /* maximum number of simultaneous HTTP connections */ 00304 static unsigned int nb_max_http_connections = 2000; 00305 static unsigned int nb_max_connections = 5; 00306 static unsigned int nb_connections; 00307 00308 static uint64_t max_bandwidth = 1000; 00309 static uint64_t current_bandwidth; 00310 00311 static int64_t cur_time; // Making this global saves on passing it around everywhere 00312 00313 static AVLFG random_state; 00314 00315 static FILE *logfile = NULL; 00316 00317 /* FIXME: make ffserver work with IPv6 */ 00318 /* resolve host with also IP address parsing */ 00319 static int resolve_host(struct in_addr *sin_addr, const char *hostname) 00320 { 00321 00322 if (!ff_inet_aton(hostname, sin_addr)) { 00323 #if HAVE_GETADDRINFO 00324 struct addrinfo *ai, *cur; 00325 struct addrinfo hints; 00326 memset(&hints, 0, sizeof(hints)); 00327 hints.ai_family = AF_INET; 00328 if (getaddrinfo(hostname, NULL, &hints, &ai)) 00329 return -1; 00330 /* getaddrinfo returns a linked list of addrinfo structs. 00331 * Even if we set ai_family = AF_INET above, make sure 00332 * that the returned one actually is of the correct type. */ 00333 for (cur = ai; cur; cur = cur->ai_next) { 00334 if (cur->ai_family == AF_INET) { 00335 *sin_addr = ((struct sockaddr_in *)cur->ai_addr)->sin_addr; 00336 freeaddrinfo(ai); 00337 return 0; 00338 } 00339 } 00340 freeaddrinfo(ai); 00341 return -1; 00342 #else 00343 struct hostent *hp; 00344 hp = gethostbyname(hostname); 00345 if (!hp) 00346 return -1; 00347 memcpy(sin_addr, hp->h_addr_list[0], sizeof(struct in_addr)); 00348 #endif 00349 } 00350 return 0; 00351 } 00352 00353 static char *ctime1(char *buf2) 00354 { 00355 time_t ti; 00356 char *p; 00357 00358 ti = time(NULL); 00359 p = ctime(&ti); 00360 strcpy(buf2, p); 00361 p = buf2 + strlen(p) - 1; 00362 if (*p == '\n') 00363 *p = '\0'; 00364 return buf2; 00365 } 00366 00367 static void http_vlog(const char *fmt, va_list vargs) 00368 { 00369 static int print_prefix = 1; 00370 if (logfile) { 00371 if (print_prefix) { 00372 char buf[32]; 00373 ctime1(buf); 00374 fprintf(logfile, "%s ", buf); 00375 } 00376 print_prefix = strstr(fmt, "\n") != NULL; 00377 vfprintf(logfile, fmt, vargs); 00378 fflush(logfile); 00379 } 00380 } 00381 00382 static void __attribute__ ((format (printf, 1, 2))) http_log(const char *fmt, ...) 00383 { 00384 va_list vargs; 00385 va_start(vargs, fmt); 00386 http_vlog(fmt, vargs); 00387 va_end(vargs); 00388 } 00389 00390 static void http_av_log(void *ptr, int level, const char *fmt, va_list vargs) 00391 { 00392 static int print_prefix = 1; 00393 AVClass *avc = ptr ? *(AVClass**)ptr : NULL; 00394 if (level > av_log_get_level()) 00395 return; 00396 if (print_prefix && avc) 00397 http_log("[%s @ %p]", avc->item_name(ptr), ptr); 00398 print_prefix = strstr(fmt, "\n") != NULL; 00399 http_vlog(fmt, vargs); 00400 } 00401 00402 static void log_connection(HTTPContext *c) 00403 { 00404 if (c->suppress_log) 00405 return; 00406 00407 http_log("%s - - [%s] \"%s %s\" %d %"PRId64"\n", 00408 inet_ntoa(c->from_addr.sin_addr), c->method, c->url, 00409 c->protocol, (c->http_error ? c->http_error : 200), c->data_count); 00410 } 00411 00412 static void update_datarate(DataRateData *drd, int64_t count) 00413 { 00414 if (!drd->time1 && !drd->count1) { 00415 drd->time1 = drd->time2 = cur_time; 00416 drd->count1 = drd->count2 = count; 00417 } else if (cur_time - drd->time2 > 5000) { 00418 drd->time1 = drd->time2; 00419 drd->count1 = drd->count2; 00420 drd->time2 = cur_time; 00421 drd->count2 = count; 00422 } 00423 } 00424 00425 /* In bytes per second */ 00426 static int compute_datarate(DataRateData *drd, int64_t count) 00427 { 00428 if (cur_time == drd->time1) 00429 return 0; 00430 00431 return ((count - drd->count1) * 1000) / (cur_time - drd->time1); 00432 } 00433 00434 00435 static void start_children(FFStream *feed) 00436 { 00437 if (no_launch) 00438 return; 00439 00440 for (; feed; feed = feed->next) { 00441 if (feed->child_argv && !feed->pid) { 00442 feed->pid_start = time(0); 00443 00444 feed->pid = fork(); 00445 00446 if (feed->pid < 0) { 00447 http_log("Unable to create children\n"); 00448 exit(1); 00449 } 00450 if (!feed->pid) { 00451 /* In child */ 00452 char pathname[1024]; 00453 char *slash; 00454 int i; 00455 00456 av_strlcpy(pathname, my_program_name, sizeof(pathname)); 00457 00458 slash = strrchr(pathname, '/'); 00459 if (!slash) 00460 slash = pathname; 00461 else 00462 slash++; 00463 strcpy(slash, "ffmpeg"); 00464 00465 http_log("Launch commandline: "); 00466 http_log("%s ", pathname); 00467 for (i = 1; feed->child_argv[i] && feed->child_argv[i][0]; i++) 00468 http_log("%s ", feed->child_argv[i]); 00469 http_log("\n"); 00470 00471 for (i = 3; i < 256; i++) 00472 close(i); 00473 00474 if (!ffserver_debug) { 00475 i = open("/dev/null", O_RDWR); 00476 if (i != -1) { 00477 dup2(i, 0); 00478 dup2(i, 1); 00479 dup2(i, 2); 00480 close(i); 00481 } 00482 } 00483 00484 /* This is needed to make relative pathnames work */ 00485 chdir(my_program_dir); 00486 00487 signal(SIGPIPE, SIG_DFL); 00488 00489 execvp(pathname, feed->child_argv); 00490 00491 _exit(1); 00492 } 00493 } 00494 } 00495 } 00496 00497 /* open a listening socket */ 00498 static int socket_open_listen(struct sockaddr_in *my_addr) 00499 { 00500 int server_fd, tmp; 00501 00502 server_fd = socket(AF_INET,SOCK_STREAM,0); 00503 if (server_fd < 0) { 00504 perror ("socket"); 00505 return -1; 00506 } 00507 00508 tmp = 1; 00509 setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp)); 00510 00511 if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) { 00512 char bindmsg[32]; 00513 snprintf(bindmsg, sizeof(bindmsg), "bind(port %d)", ntohs(my_addr->sin_port)); 00514 perror (bindmsg); 00515 closesocket(server_fd); 00516 return -1; 00517 } 00518 00519 if (listen (server_fd, 5) < 0) { 00520 perror ("listen"); 00521 closesocket(server_fd); 00522 return -1; 00523 } 00524 ff_socket_nonblock(server_fd, 1); 00525 00526 return server_fd; 00527 } 00528 00529 /* start all multicast streams */ 00530 static void start_multicast(void) 00531 { 00532 FFStream *stream; 00533 char session_id[32]; 00534 HTTPContext *rtp_c; 00535 struct sockaddr_in dest_addr; 00536 int default_port, stream_index; 00537 00538 default_port = 6000; 00539 for(stream = first_stream; stream != NULL; stream = stream->next) { 00540 if (stream->is_multicast) { 00541 /* open the RTP connection */ 00542 snprintf(session_id, sizeof(session_id), "%08x%08x", 00543 av_lfg_get(&random_state), av_lfg_get(&random_state)); 00544 00545 /* choose a port if none given */ 00546 if (stream->multicast_port == 0) { 00547 stream->multicast_port = default_port; 00548 default_port += 100; 00549 } 00550 00551 dest_addr.sin_family = AF_INET; 00552 dest_addr.sin_addr = stream->multicast_ip; 00553 dest_addr.sin_port = htons(stream->multicast_port); 00554 00555 rtp_c = rtp_new_connection(&dest_addr, stream, session_id, 00556 RTSP_LOWER_TRANSPORT_UDP_MULTICAST); 00557 if (!rtp_c) 00558 continue; 00559 00560 if (open_input_stream(rtp_c, "") < 0) { 00561 http_log("Could not open input stream for stream '%s'\n", 00562 stream->filename); 00563 continue; 00564 } 00565 00566 /* open each RTP stream */ 00567 for(stream_index = 0; stream_index < stream->nb_streams; 00568 stream_index++) { 00569 dest_addr.sin_port = htons(stream->multicast_port + 00570 2 * stream_index); 00571 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, NULL) < 0) { 00572 http_log("Could not open output stream '%s/streamid=%d'\n", 00573 stream->filename, stream_index); 00574 exit(1); 00575 } 00576 } 00577 00578 /* change state to send data */ 00579 rtp_c->state = HTTPSTATE_SEND_DATA; 00580 } 00581 } 00582 } 00583 00584 /* main loop of the http server */ 00585 static int http_server(void) 00586 { 00587 int server_fd = 0, rtsp_server_fd = 0; 00588 int ret, delay, delay1; 00589 struct pollfd *poll_table, *poll_entry; 00590 HTTPContext *c, *c_next; 00591 00592 if(!(poll_table = av_mallocz((nb_max_http_connections + 2)*sizeof(*poll_table)))) { 00593 http_log("Impossible to allocate a poll table handling %d connections.\n", nb_max_http_connections); 00594 return -1; 00595 } 00596 00597 if (my_http_addr.sin_port) { 00598 server_fd = socket_open_listen(&my_http_addr); 00599 if (server_fd < 0) 00600 return -1; 00601 } 00602 00603 if (my_rtsp_addr.sin_port) { 00604 rtsp_server_fd = socket_open_listen(&my_rtsp_addr); 00605 if (rtsp_server_fd < 0) 00606 return -1; 00607 } 00608 00609 if (!rtsp_server_fd && !server_fd) { 00610 http_log("HTTP and RTSP disabled.\n"); 00611 return -1; 00612 } 00613 00614 http_log("FFserver started.\n"); 00615 00616 start_children(first_feed); 00617 00618 start_multicast(); 00619 00620 for(;;) { 00621 poll_entry = poll_table; 00622 if (server_fd) { 00623 poll_entry->fd = server_fd; 00624 poll_entry->events = POLLIN; 00625 poll_entry++; 00626 } 00627 if (rtsp_server_fd) { 00628 poll_entry->fd = rtsp_server_fd; 00629 poll_entry->events = POLLIN; 00630 poll_entry++; 00631 } 00632 00633 /* wait for events on each HTTP handle */ 00634 c = first_http_ctx; 00635 delay = 1000; 00636 while (c != NULL) { 00637 int fd; 00638 fd = c->fd; 00639 switch(c->state) { 00640 case HTTPSTATE_SEND_HEADER: 00641 case RTSPSTATE_SEND_REPLY: 00642 case RTSPSTATE_SEND_PACKET: 00643 c->poll_entry = poll_entry; 00644 poll_entry->fd = fd; 00645 poll_entry->events = POLLOUT; 00646 poll_entry++; 00647 break; 00648 case HTTPSTATE_SEND_DATA_HEADER: 00649 case HTTPSTATE_SEND_DATA: 00650 case HTTPSTATE_SEND_DATA_TRAILER: 00651 if (!c->is_packetized) { 00652 /* for TCP, we output as much as we can (may need to put a limit) */ 00653 c->poll_entry = poll_entry; 00654 poll_entry->fd = fd; 00655 poll_entry->events = POLLOUT; 00656 poll_entry++; 00657 } else { 00658 /* when ffserver is doing the timing, we work by 00659 looking at which packet need to be sent every 00660 10 ms */ 00661 delay1 = 10; /* one tick wait XXX: 10 ms assumed */ 00662 if (delay1 < delay) 00663 delay = delay1; 00664 } 00665 break; 00666 case HTTPSTATE_WAIT_REQUEST: 00667 case HTTPSTATE_RECEIVE_DATA: 00668 case HTTPSTATE_WAIT_FEED: 00669 case RTSPSTATE_WAIT_REQUEST: 00670 /* need to catch errors */ 00671 c->poll_entry = poll_entry; 00672 poll_entry->fd = fd; 00673 poll_entry->events = POLLIN;/* Maybe this will work */ 00674 poll_entry++; 00675 break; 00676 default: 00677 c->poll_entry = NULL; 00678 break; 00679 } 00680 c = c->next; 00681 } 00682 00683 /* wait for an event on one connection. We poll at least every 00684 second to handle timeouts */ 00685 do { 00686 ret = poll(poll_table, poll_entry - poll_table, delay); 00687 if (ret < 0 && ff_neterrno() != FF_NETERROR(EAGAIN) && 00688 ff_neterrno() != FF_NETERROR(EINTR)) 00689 return -1; 00690 } while (ret < 0); 00691 00692 cur_time = av_gettime() / 1000; 00693 00694 if (need_to_start_children) { 00695 need_to_start_children = 0; 00696 start_children(first_feed); 00697 } 00698 00699 /* now handle the events */ 00700 for(c = first_http_ctx; c != NULL; c = c_next) { 00701 c_next = c->next; 00702 if (handle_connection(c) < 0) { 00703 /* close and free the connection */ 00704 log_connection(c); 00705 close_connection(c); 00706 } 00707 } 00708 00709 poll_entry = poll_table; 00710 if (server_fd) { 00711 /* new HTTP connection request ? */ 00712 if (poll_entry->revents & POLLIN) 00713 new_connection(server_fd, 0); 00714 poll_entry++; 00715 } 00716 if (rtsp_server_fd) { 00717 /* new RTSP connection request ? */ 00718 if (poll_entry->revents & POLLIN) 00719 new_connection(rtsp_server_fd, 1); 00720 } 00721 } 00722 } 00723 00724 /* start waiting for a new HTTP/RTSP request */ 00725 static void start_wait_request(HTTPContext *c, int is_rtsp) 00726 { 00727 c->buffer_ptr = c->buffer; 00728 c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */ 00729 00730 if (is_rtsp) { 00731 c->timeout = cur_time + RTSP_REQUEST_TIMEOUT; 00732 c->state = RTSPSTATE_WAIT_REQUEST; 00733 } else { 00734 c->timeout = cur_time + HTTP_REQUEST_TIMEOUT; 00735 c->state = HTTPSTATE_WAIT_REQUEST; 00736 } 00737 } 00738 00739 static void http_send_too_busy_reply(int fd) 00740 { 00741 char buffer[300]; 00742 int len = snprintf(buffer, sizeof(buffer), 00743 "HTTP/1.0 200 Server too busy\r\n" 00744 "Content-type: text/html\r\n" 00745 "\r\n" 00746 "<html><head><title>Too busy</title></head><body>\r\n" 00747 "<p>The server is too busy to serve your request at this time.</p>\r\n" 00748 "<p>The number of current connections is %d, and this exceeds the limit of %d.</p>\r\n" 00749 "</body></html>\r\n", 00750 nb_connections, nb_max_connections); 00751 send(fd, buffer, len, 0); 00752 } 00753 00754 00755 static void new_connection(int server_fd, int is_rtsp) 00756 { 00757 struct sockaddr_in from_addr; 00758 int fd, len; 00759 HTTPContext *c = NULL; 00760 00761 len = sizeof(from_addr); 00762 fd = accept(server_fd, (struct sockaddr *)&from_addr, 00763 &len); 00764 if (fd < 0) { 00765 http_log("error during accept %s\n", strerror(errno)); 00766 return; 00767 } 00768 ff_socket_nonblock(fd, 1); 00769 00770 if (nb_connections >= nb_max_connections) { 00771 http_send_too_busy_reply(fd); 00772 goto fail; 00773 } 00774 00775 /* add a new connection */ 00776 c = av_mallocz(sizeof(HTTPContext)); 00777 if (!c) 00778 goto fail; 00779 00780 c->fd = fd; 00781 c->poll_entry = NULL; 00782 c->from_addr = from_addr; 00783 c->buffer_size = IOBUFFER_INIT_SIZE; 00784 c->buffer = av_malloc(c->buffer_size); 00785 if (!c->buffer) 00786 goto fail; 00787 00788 c->next = first_http_ctx; 00789 first_http_ctx = c; 00790 nb_connections++; 00791 00792 start_wait_request(c, is_rtsp); 00793 00794 return; 00795 00796 fail: 00797 if (c) { 00798 av_free(c->buffer); 00799 av_free(c); 00800 } 00801 closesocket(fd); 00802 } 00803 00804 static void close_connection(HTTPContext *c) 00805 { 00806 HTTPContext **cp, *c1; 00807 int i, nb_streams; 00808 AVFormatContext *ctx; 00809 URLContext *h; 00810 AVStream *st; 00811 00812 /* remove connection from list */ 00813 cp = &first_http_ctx; 00814 while ((*cp) != NULL) { 00815 c1 = *cp; 00816 if (c1 == c) 00817 *cp = c->next; 00818 else 00819 cp = &c1->next; 00820 } 00821 00822 /* remove references, if any (XXX: do it faster) */ 00823 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) { 00824 if (c1->rtsp_c == c) 00825 c1->rtsp_c = NULL; 00826 } 00827 00828 /* remove connection associated resources */ 00829 if (c->fd >= 0) 00830 closesocket(c->fd); 00831 if (c->fmt_in) { 00832 /* close each frame parser */ 00833 for(i=0;i<c->fmt_in->nb_streams;i++) { 00834 st = c->fmt_in->streams[i]; 00835 if (st->codec->codec) 00836 avcodec_close(st->codec); 00837 } 00838 av_close_input_file(c->fmt_in); 00839 } 00840 00841 /* free RTP output streams if any */ 00842 nb_streams = 0; 00843 if (c->stream) 00844 nb_streams = c->stream->nb_streams; 00845 00846 for(i=0;i<nb_streams;i++) { 00847 ctx = c->rtp_ctx[i]; 00848 if (ctx) { 00849 av_write_trailer(ctx); 00850 av_metadata_free(&ctx->metadata); 00851 av_free(ctx->streams[0]); 00852 av_free(ctx); 00853 } 00854 h = c->rtp_handles[i]; 00855 if (h) 00856 url_close(h); 00857 } 00858 00859 ctx = &c->fmt_ctx; 00860 00861 if (!c->last_packet_sent && c->state == HTTPSTATE_SEND_DATA_TRAILER) { 00862 if (ctx->oformat) { 00863 /* prepare header */ 00864 if (url_open_dyn_buf(&ctx->pb) >= 0) { 00865 av_write_trailer(ctx); 00866 av_freep(&c->pb_buffer); 00867 url_close_dyn_buf(ctx->pb, &c->pb_buffer); 00868 } 00869 } 00870 } 00871 00872 for(i=0; i<ctx->nb_streams; i++) 00873 av_free(ctx->streams[i]); 00874 00875 if (c->stream && !c->post && c->stream->stream_type == STREAM_TYPE_LIVE) 00876 current_bandwidth -= c->stream->bandwidth; 00877 00878 /* signal that there is no feed if we are the feeder socket */ 00879 if (c->state == HTTPSTATE_RECEIVE_DATA && c->stream) { 00880 c->stream->feed_opened = 0; 00881 close(c->feed_fd); 00882 } 00883 00884 av_freep(&c->pb_buffer); 00885 av_freep(&c->packet_buffer); 00886 av_free(c->buffer); 00887 av_free(c); 00888 nb_connections--; 00889 } 00890 00891 static int handle_connection(HTTPContext *c) 00892 { 00893 int len, ret; 00894 00895 switch(c->state) { 00896 case HTTPSTATE_WAIT_REQUEST: 00897 case RTSPSTATE_WAIT_REQUEST: 00898 /* timeout ? */ 00899 if ((c->timeout - cur_time) < 0) 00900 return -1; 00901 if (c->poll_entry->revents & (POLLERR | POLLHUP)) 00902 return -1; 00903 00904 /* no need to read if no events */ 00905 if (!(c->poll_entry->revents & POLLIN)) 00906 return 0; 00907 /* read the data */ 00908 read_loop: 00909 len = recv(c->fd, c->buffer_ptr, 1, 0); 00910 if (len < 0) { 00911 if (ff_neterrno() != FF_NETERROR(EAGAIN) && 00912 ff_neterrno() != FF_NETERROR(EINTR)) 00913 return -1; 00914 } else if (len == 0) { 00915 return -1; 00916 } else { 00917 /* search for end of request. */ 00918 uint8_t *ptr; 00919 c->buffer_ptr += len; 00920 ptr = c->buffer_ptr; 00921 if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) || 00922 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) { 00923 /* request found : parse it and reply */ 00924 if (c->state == HTTPSTATE_WAIT_REQUEST) { 00925 ret = http_parse_request(c); 00926 } else { 00927 ret = rtsp_parse_request(c); 00928 } 00929 if (ret < 0) 00930 return -1; 00931 } else if (ptr >= c->buffer_end) { 00932 /* request too long: cannot do anything */ 00933 return -1; 00934 } else goto read_loop; 00935 } 00936 break; 00937 00938 case HTTPSTATE_SEND_HEADER: 00939 if (c->poll_entry->revents & (POLLERR | POLLHUP)) 00940 return -1; 00941 00942 /* no need to write if no events */ 00943 if (!(c->poll_entry->revents & POLLOUT)) 00944 return 0; 00945 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0); 00946 if (len < 0) { 00947 if (ff_neterrno() != FF_NETERROR(EAGAIN) && 00948 ff_neterrno() != FF_NETERROR(EINTR)) { 00949 /* error : close connection */ 00950 av_freep(&c->pb_buffer); 00951 return -1; 00952 } 00953 } else { 00954 c->buffer_ptr += len; 00955 if (c->stream) 00956 c->stream->bytes_served += len; 00957 c->data_count += len; 00958 if (c->buffer_ptr >= c->buffer_end) { 00959 av_freep(&c->pb_buffer); 00960 /* if error, exit */ 00961 if (c->http_error) 00962 return -1; 00963 /* all the buffer was sent : synchronize to the incoming stream */ 00964 c->state = HTTPSTATE_SEND_DATA_HEADER; 00965 c->buffer_ptr = c->buffer_end = c->buffer; 00966 } 00967 } 00968 break; 00969 00970 case HTTPSTATE_SEND_DATA: 00971 case HTTPSTATE_SEND_DATA_HEADER: 00972 case HTTPSTATE_SEND_DATA_TRAILER: 00973 /* for packetized output, we consider we can always write (the 00974 input streams sets the speed). It may be better to verify 00975 that we do not rely too much on the kernel queues */ 00976 if (!c->is_packetized) { 00977 if (c->poll_entry->revents & (POLLERR | POLLHUP)) 00978 return -1; 00979 00980 /* no need to read if no events */ 00981 if (!(c->poll_entry->revents & POLLOUT)) 00982 return 0; 00983 } 00984 if (http_send_data(c) < 0) 00985 return -1; 00986 /* close connection if trailer sent */ 00987 if (c->state == HTTPSTATE_SEND_DATA_TRAILER) 00988 return -1; 00989 break; 00990 case HTTPSTATE_RECEIVE_DATA: 00991 /* no need to read if no events */ 00992 if (c->poll_entry->revents & (POLLERR | POLLHUP)) 00993 return -1; 00994 if (!(c->poll_entry->revents & POLLIN)) 00995 return 0; 00996 if (http_receive_data(c) < 0) 00997 return -1; 00998 break; 00999 case HTTPSTATE_WAIT_FEED: 01000 /* no need to read if no events */ 01001 if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP)) 01002 return -1; 01003 01004 /* nothing to do, we'll be waken up by incoming feed packets */ 01005 break; 01006 01007 case RTSPSTATE_SEND_REPLY: 01008 if (c->poll_entry->revents & (POLLERR | POLLHUP)) { 01009 av_freep(&c->pb_buffer); 01010 return -1; 01011 } 01012 /* no need to write if no events */ 01013 if (!(c->poll_entry->revents & POLLOUT)) 01014 return 0; 01015 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0); 01016 if (len < 0) { 01017 if (ff_neterrno() != FF_NETERROR(EAGAIN) && 01018 ff_neterrno() != FF_NETERROR(EINTR)) { 01019 /* error : close connection */ 01020 av_freep(&c->pb_buffer); 01021 return -1; 01022 } 01023 } else { 01024 c->buffer_ptr += len; 01025 c->data_count += len; 01026 if (c->buffer_ptr >= c->buffer_end) { 01027 /* all the buffer was sent : wait for a new request */ 01028 av_freep(&c->pb_buffer); 01029 start_wait_request(c, 1); 01030 } 01031 } 01032 break; 01033 case RTSPSTATE_SEND_PACKET: 01034 if (c->poll_entry->revents & (POLLERR | POLLHUP)) { 01035 av_freep(&c->packet_buffer); 01036 return -1; 01037 } 01038 /* no need to write if no events */ 01039 if (!(c->poll_entry->revents & POLLOUT)) 01040 return 0; 01041 len = send(c->fd, c->packet_buffer_ptr, 01042 c->packet_buffer_end - c->packet_buffer_ptr, 0); 01043 if (len < 0) { 01044 if (ff_neterrno() != FF_NETERROR(EAGAIN) && 01045 ff_neterrno() != FF_NETERROR(EINTR)) { 01046 /* error : close connection */ 01047 av_freep(&c->packet_buffer); 01048 return -1; 01049 } 01050 } else { 01051 c->packet_buffer_ptr += len; 01052 if (c->packet_buffer_ptr >= c->packet_buffer_end) { 01053 /* all the buffer was sent : wait for a new request */ 01054 av_freep(&c->packet_buffer); 01055 c->state = RTSPSTATE_WAIT_REQUEST; 01056 } 01057 } 01058 break; 01059 case HTTPSTATE_READY: 01060 /* nothing to do */ 01061 break; 01062 default: 01063 return -1; 01064 } 01065 return 0; 01066 } 01067 01068 static int extract_rates(char *rates, int ratelen, const char *request) 01069 { 01070 const char *p; 01071 01072 for (p = request; *p && *p != '\r' && *p != '\n'; ) { 01073 if (strncasecmp(p, "Pragma:", 7) == 0) { 01074 const char *q = p + 7; 01075 01076 while (*q && *q != '\n' && isspace(*q)) 01077 q++; 01078 01079 if (strncasecmp(q, "stream-switch-entry=", 20) == 0) { 01080 int stream_no; 01081 int rate_no; 01082 01083 q += 20; 01084 01085 memset(rates, 0xff, ratelen); 01086 01087 while (1) { 01088 while (*q && *q != '\n' && *q != ':') 01089 q++; 01090 01091 if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2) 01092 break; 01093 01094 stream_no--; 01095 if (stream_no < ratelen && stream_no >= 0) 01096 rates[stream_no] = rate_no; 01097 01098 while (*q && *q != '\n' && !isspace(*q)) 01099 q++; 01100 } 01101 01102 return 1; 01103 } 01104 } 01105 p = strchr(p, '\n'); 01106 if (!p) 01107 break; 01108 01109 p++; 01110 } 01111 01112 return 0; 01113 } 01114 01115 static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate) 01116 { 01117 int i; 01118 int best_bitrate = 100000000; 01119 int best = -1; 01120 01121 for (i = 0; i < feed->nb_streams; i++) { 01122 AVCodecContext *feed_codec = feed->streams[i]->codec; 01123 01124 if (feed_codec->codec_id != codec->codec_id || 01125 feed_codec->sample_rate != codec->sample_rate || 01126 feed_codec->width != codec->width || 01127 feed_codec->height != codec->height) 01128 continue; 01129 01130 /* Potential stream */ 01131 01132 /* We want the fastest stream less than bit_rate, or the slowest 01133 * faster than bit_rate 01134 */ 01135 01136 if (feed_codec->bit_rate <= bit_rate) { 01137 if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) { 01138 best_bitrate = feed_codec->bit_rate; 01139 best = i; 01140 } 01141 } else { 01142 if (feed_codec->bit_rate < best_bitrate) { 01143 best_bitrate = feed_codec->bit_rate; 01144 best = i; 01145 } 01146 } 01147 } 01148 01149 return best; 01150 } 01151 01152 static int modify_current_stream(HTTPContext *c, char *rates) 01153 { 01154 int i; 01155 FFStream *req = c->stream; 01156 int action_required = 0; 01157 01158 /* Not much we can do for a feed */ 01159 if (!req->feed) 01160 return 0; 01161 01162 for (i = 0; i < req->nb_streams; i++) { 01163 AVCodecContext *codec = req->streams[i]->codec; 01164 01165 switch(rates[i]) { 01166 case 0: 01167 c->switch_feed_streams[i] = req->feed_streams[i]; 01168 break; 01169 case 1: 01170 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2); 01171 break; 01172 case 2: 01173 /* Wants off or slow */ 01174 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4); 01175 #ifdef WANTS_OFF 01176 /* This doesn't work well when it turns off the only stream! */ 01177 c->switch_feed_streams[i] = -2; 01178 c->feed_streams[i] = -2; 01179 #endif 01180 break; 01181 } 01182 01183 if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i]) 01184 action_required = 1; 01185 } 01186 01187 return action_required; 01188 } 01189 01190 01191 static void do_switch_stream(HTTPContext *c, int i) 01192 { 01193 if (c->switch_feed_streams[i] >= 0) { 01194 #ifdef PHILIP 01195 c->feed_streams[i] = c->switch_feed_streams[i]; 01196 #endif 01197 01198 /* Now update the stream */ 01199 } 01200 c->switch_feed_streams[i] = -1; 01201 } 01202 01203 /* XXX: factorize in utils.c ? */ 01204 /* XXX: take care with different space meaning */ 01205 static void skip_spaces(const char **pp) 01206 { 01207 const char *p; 01208 p = *pp; 01209 while (*p == ' ' || *p == '\t') 01210 p++; 01211 *pp = p; 01212 } 01213 01214 static void get_word(char *buf, int buf_size, const char **pp) 01215 { 01216 const char *p; 01217 char *q; 01218 01219 p = *pp; 01220 skip_spaces(&p); 01221 q = buf; 01222 while (!isspace(*p) && *p != '\0') { 01223 if ((q - buf) < buf_size - 1) 01224 *q++ = *p; 01225 p++; 01226 } 01227 if (buf_size > 0) 01228 *q = '\0'; 01229 *pp = p; 01230 } 01231 01232 static void get_arg(char *buf, int buf_size, const char **pp) 01233 { 01234 const char *p; 01235 char *q; 01236 int quote; 01237 01238 p = *pp; 01239 while (isspace(*p)) p++; 01240 q = buf; 01241 quote = 0; 01242 if (*p == '\"' || *p == '\'') 01243 quote = *p++; 01244 for(;;) { 01245 if (quote) { 01246 if (*p == quote) 01247 break; 01248 } else { 01249 if (isspace(*p)) 01250 break; 01251 } 01252 if (*p == '\0') 01253 break; 01254 if ((q - buf) < buf_size - 1) 01255 *q++ = *p; 01256 p++; 01257 } 01258 *q = '\0'; 01259 if (quote && *p == quote) 01260 p++; 01261 *pp = p; 01262 } 01263 01264 static void parse_acl_row(FFStream *stream, FFStream* feed, IPAddressACL *ext_acl, 01265 const char *p, const char *filename, int line_num) 01266 { 01267 char arg[1024]; 01268 IPAddressACL acl; 01269 int errors = 0; 01270 01271 get_arg(arg, sizeof(arg), &p); 01272 if (strcasecmp(arg, "allow") == 0) 01273 acl.action = IP_ALLOW; 01274 else if (strcasecmp(arg, "deny") == 0) 01275 acl.action = IP_DENY; 01276 else { 01277 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n", 01278 filename, line_num, arg); 01279 errors++; 01280 } 01281 01282 get_arg(arg, sizeof(arg), &p); 01283 01284 if (resolve_host(&acl.first, arg) != 0) { 01285 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n", 01286 filename, line_num, arg); 01287 errors++; 01288 } else 01289 acl.last = acl.first; 01290 01291 get_arg(arg, sizeof(arg), &p); 01292 01293 if (arg[0]) { 01294 if (resolve_host(&acl.last, arg) != 0) { 01295 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n", 01296 filename, line_num, arg); 01297 errors++; 01298 } 01299 } 01300 01301 if (!errors) { 01302 IPAddressACL *nacl = av_mallocz(sizeof(*nacl)); 01303 IPAddressACL **naclp = 0; 01304 01305 acl.next = 0; 01306 *nacl = acl; 01307 01308 if (stream) 01309 naclp = &stream->acl; 01310 else if (feed) 01311 naclp = &feed->acl; 01312 else if (ext_acl) 01313 naclp = &ext_acl; 01314 else { 01315 fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n", 01316 filename, line_num); 01317 errors++; 01318 } 01319 01320 if (naclp) { 01321 while (*naclp) 01322 naclp = &(*naclp)->next; 01323 01324 *naclp = nacl; 01325 } 01326 } 01327 } 01328 01329 01330 static IPAddressACL* parse_dynamic_acl(FFStream *stream, HTTPContext *c) 01331 { 01332 FILE* f; 01333 char line[1024]; 01334 char cmd[1024]; 01335 IPAddressACL *acl = NULL; 01336 int line_num = 0; 01337 const char *p; 01338 01339 f = fopen(stream->dynamic_acl, "r"); 01340 if (!f) { 01341 perror(stream->dynamic_acl); 01342 return NULL; 01343 } 01344 01345 acl = av_mallocz(sizeof(IPAddressACL)); 01346 01347 /* Build ACL */ 01348 for(;;) { 01349 if (fgets(line, sizeof(line), f) == NULL) 01350 break; 01351 line_num++; 01352 p = line; 01353 while (isspace(*p)) 01354 p++; 01355 if (*p == '\0' || *p == '#') 01356 continue; 01357 get_arg(cmd, sizeof(cmd), &p); 01358 01359 if (!strcasecmp(cmd, "ACL")) 01360 parse_acl_row(NULL, NULL, acl, p, stream->dynamic_acl, line_num); 01361 } 01362 fclose(f); 01363 return acl; 01364 } 01365 01366 01367 static void free_acl_list(IPAddressACL *in_acl) 01368 { 01369 IPAddressACL *pacl,*pacl2; 01370 01371 pacl = in_acl; 01372 while(pacl) { 01373 pacl2 = pacl; 01374 pacl = pacl->next; 01375 av_freep(pacl2); 01376 } 01377 } 01378 01379 static int validate_acl_list(IPAddressACL *in_acl, HTTPContext *c) 01380 { 01381 enum IPAddressAction last_action = IP_DENY; 01382 IPAddressACL *acl; 01383 struct in_addr *src = &c->from_addr.sin_addr; 01384 unsigned long src_addr = src->s_addr; 01385 01386 for (acl = in_acl; acl; acl = acl->next) { 01387 if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr) 01388 return (acl->action == IP_ALLOW) ? 1 : 0; 01389 last_action = acl->action; 01390 } 01391 01392 /* Nothing matched, so return not the last action */ 01393 return (last_action == IP_DENY) ? 1 : 0; 01394 } 01395 01396 static int validate_acl(FFStream *stream, HTTPContext *c) 01397 { 01398 int ret = 0; 01399 IPAddressACL *acl; 01400 01401 01402 /* if stream->acl is null validate_acl_list will return 1 */ 01403 ret = validate_acl_list(stream->acl, c); 01404 01405 if (stream->dynamic_acl[0]) { 01406 acl = parse_dynamic_acl(stream, c); 01407 01408 ret = validate_acl_list(acl, c); 01409 01410 free_acl_list(acl); 01411 } 01412 01413 return ret; 01414 } 01415 01416 /* compute the real filename of a file by matching it without its 01417 extensions to all the stream filenames */ 01418 static void compute_real_filename(char *filename, int max_size) 01419 { 01420 char file1[1024]; 01421 char file2[1024]; 01422 char *p; 01423 FFStream *stream; 01424 01425 /* compute filename by matching without the file extensions */ 01426 av_strlcpy(file1, filename, sizeof(file1)); 01427 p = strrchr(file1, '.'); 01428 if (p) 01429 *p = '\0'; 01430 for(stream = first_stream; stream != NULL; stream = stream->next) { 01431 av_strlcpy(file2, stream->filename, sizeof(file2)); 01432 p = strrchr(file2, '.'); 01433 if (p) 01434 *p = '\0'; 01435 if (!strcmp(file1, file2)) { 01436 av_strlcpy(filename, stream->filename, max_size); 01437 break; 01438 } 01439 } 01440 } 01441 01442 enum RedirType { 01443 REDIR_NONE, 01444 REDIR_ASX, 01445 REDIR_RAM, 01446 REDIR_ASF, 01447 REDIR_RTSP, 01448 REDIR_SDP, 01449 }; 01450 01451 /* parse http request and prepare header */ 01452 static int http_parse_request(HTTPContext *c) 01453 { 01454 char *p; 01455 enum RedirType redir_type; 01456 char cmd[32]; 01457 char info[1024], filename[1024]; 01458 char url[1024], *q; 01459 char protocol[32]; 01460 char msg[1024]; 01461 const char *mime_type; 01462 FFStream *stream; 01463 int i; 01464 char ratebuf[32]; 01465 char *useragent = 0; 01466 01467 p = c->buffer; 01468 get_word(cmd, sizeof(cmd), (const char **)&p); 01469 av_strlcpy(c->method, cmd, sizeof(c->method)); 01470 01471 if (!strcmp(cmd, "GET")) 01472 c->post = 0; 01473 else if (!strcmp(cmd, "POST")) 01474 c->post = 1; 01475 else 01476 return -1; 01477 01478 get_word(url, sizeof(url), (const char **)&p); 01479 av_strlcpy(c->url, url, sizeof(c->url)); 01480 01481 get_word(protocol, sizeof(protocol), (const char **)&p); 01482 if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1")) 01483 return -1; 01484 01485 av_strlcpy(c->protocol, protocol, sizeof(c->protocol)); 01486 01487 if (ffserver_debug) 01488 http_log("%s - - New connection: %s %s\n", inet_ntoa(c->from_addr.sin_addr), cmd, url); 01489 01490 /* find the filename and the optional info string in the request */ 01491 p = strchr(url, '?'); 01492 if (p) { 01493 av_strlcpy(info, p, sizeof(info)); 01494 *p = '\0'; 01495 } else 01496 info[0] = '\0'; 01497 01498 av_strlcpy(filename, url + ((*url == '/') ? 1 : 0), sizeof(filename)-1); 01499 01500 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) { 01501 if (strncasecmp(p, "User-Agent:", 11) == 0) { 01502 useragent = p + 11; 01503 if (*useragent && *useragent != '\n' && isspace(*useragent)) 01504 useragent++; 01505 break; 01506 } 01507 p = strchr(p, '\n'); 01508 if (!p) 01509 break; 01510 01511 p++; 01512 } 01513 01514 redir_type = REDIR_NONE; 01515 if (av_match_ext(filename, "asx")) { 01516 redir_type = REDIR_ASX; 01517 filename[strlen(filename)-1] = 'f'; 01518 } else if (av_match_ext(filename, "asf") && 01519 (!useragent || strncasecmp(useragent, "NSPlayer", 8) != 0)) { 01520 /* if this isn't WMP or lookalike, return the redirector file */ 01521 redir_type = REDIR_ASF; 01522 } else if (av_match_ext(filename, "rpm,ram")) { 01523 redir_type = REDIR_RAM; 01524 strcpy(filename + strlen(filename)-2, "m"); 01525 } else if (av_match_ext(filename, "rtsp")) { 01526 redir_type = REDIR_RTSP; 01527 compute_real_filename(filename, sizeof(filename) - 1); 01528 } else if (av_match_ext(filename, "sdp")) { 01529 redir_type = REDIR_SDP; 01530 compute_real_filename(filename, sizeof(filename) - 1); 01531 } 01532 01533 // "redirect" / request to index.html 01534 if (!strlen(filename)) 01535 av_strlcpy(filename, "index.html", sizeof(filename) - 1); 01536 01537 stream = first_stream; 01538 while (stream != NULL) { 01539 if (!strcmp(stream->filename, filename) && validate_acl(stream, c)) 01540 break; 01541 stream = stream->next; 01542 } 01543 if (stream == NULL) { 01544 snprintf(msg, sizeof(msg), "File '%s' not found", url); 01545 http_log("File '%s' not found\n", url); 01546 goto send_error; 01547 } 01548 01549 c->stream = stream; 01550 memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams)); 01551 memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams)); 01552 01553 if (stream->stream_type == STREAM_TYPE_REDIRECT) { 01554 c->http_error = 301; 01555 q = c->buffer; 01556 q += snprintf(q, c->buffer_size, 01557 "HTTP/1.0 301 Moved\r\n" 01558 "Location: %s\r\n" 01559 "Content-type: text/html\r\n" 01560 "\r\n" 01561 "<html><head><title>Moved</title></head><body>\r\n" 01562 "You should be <a href=\"%s\">redirected</a>.\r\n" 01563 "</body></html>\r\n", stream->feed_filename, stream->feed_filename); 01564 /* prepare output buffer */ 01565 c->buffer_ptr = c->buffer; 01566 c->buffer_end = q; 01567 c->state = HTTPSTATE_SEND_HEADER; 01568 return 0; 01569 } 01570 01571 /* If this is WMP, get the rate information */ 01572 if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) { 01573 if (modify_current_stream(c, ratebuf)) { 01574 for (i = 0; i < FF_ARRAY_ELEMS(c->feed_streams); i++) { 01575 if (c->switch_feed_streams[i] >= 0) 01576 do_switch_stream(c, i); 01577 } 01578 } 01579 } 01580 01581 if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE) 01582 current_bandwidth += stream->bandwidth; 01583 01584 /* If already streaming this feed, do not let start another feeder. */ 01585 if (stream->feed_opened) { 01586 snprintf(msg, sizeof(msg), "This feed is already being received."); 01587 http_log("Feed '%s' already being received\n", stream->feed_filename); 01588 goto send_error; 01589 } 01590 01591 if (c->post == 0 && max_bandwidth < current_bandwidth) { 01592 c->http_error = 200; 01593 q = c->buffer; 01594 q += snprintf(q, c->buffer_size, 01595 "HTTP/1.0 200 Server too busy\r\n" 01596 "Content-type: text/html\r\n" 01597 "\r\n" 01598 "<html><head><title>Too busy</title></head><body>\r\n" 01599 "<p>The server is too busy to serve your request at this time.</p>\r\n" 01600 "<p>The bandwidth being served (including your stream) is %"PRIu64"kbit/sec, " 01601 "and this exceeds the limit of %"PRIu64"kbit/sec.</p>\r\n" 01602 "</body></html>\r\n", current_bandwidth, max_bandwidth); 01603 /* prepare output buffer */ 01604 c->buffer_ptr = c->buffer; 01605 c->buffer_end = q; 01606 c->state = HTTPSTATE_SEND_HEADER; 01607 return 0; 01608 } 01609 01610 if (redir_type != REDIR_NONE) { 01611 char *hostinfo = 0; 01612 01613 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) { 01614 if (strncasecmp(p, "Host:", 5) == 0) { 01615 hostinfo = p + 5; 01616 break; 01617 } 01618 p = strchr(p, '\n'); 01619 if (!p) 01620 break; 01621 01622 p++; 01623 } 01624 01625 if (hostinfo) { 01626 char *eoh; 01627 char hostbuf[260]; 01628 01629 while (isspace(*hostinfo)) 01630 hostinfo++; 01631 01632 eoh = strchr(hostinfo, '\n'); 01633 if (eoh) { 01634 if (eoh[-1] == '\r') 01635 eoh--; 01636 01637 if (eoh - hostinfo < sizeof(hostbuf) - 1) { 01638 memcpy(hostbuf, hostinfo, eoh - hostinfo); 01639 hostbuf[eoh - hostinfo] = 0; 01640 01641 c->http_error = 200; 01642 q = c->buffer; 01643 switch(redir_type) { 01644 case REDIR_ASX: 01645 q += snprintf(q, c->buffer_size, 01646 "HTTP/1.0 200 ASX Follows\r\n" 01647 "Content-type: video/x-ms-asf\r\n" 01648 "\r\n" 01649 "<ASX Version=\"3\">\r\n" 01650 //"<!-- Autogenerated by ffserver -->\r\n" 01651 "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n" 01652 "</ASX>\r\n", hostbuf, filename, info); 01653 break; 01654 case REDIR_RAM: 01655 q += snprintf(q, c->buffer_size, 01656 "HTTP/1.0 200 RAM Follows\r\n" 01657 "Content-type: audio/x-pn-realaudio\r\n" 01658 "\r\n" 01659 "# Autogenerated by ffserver\r\n" 01660 "http://%s/%s%s\r\n", hostbuf, filename, info); 01661 break; 01662 case REDIR_ASF: 01663 q += snprintf(q, c->buffer_size, 01664 "HTTP/1.0 200 ASF Redirect follows\r\n" 01665 "Content-type: video/x-ms-asf\r\n" 01666 "\r\n" 01667 "[Reference]\r\n" 01668 "Ref1=http://%s/%s%s\r\n", hostbuf, filename, info); 01669 break; 01670 case REDIR_RTSP: 01671 { 01672 char hostname[256], *p; 01673 /* extract only hostname */ 01674 av_strlcpy(hostname, hostbuf, sizeof(hostname)); 01675 p = strrchr(hostname, ':'); 01676 if (p) 01677 *p = '\0'; 01678 q += snprintf(q, c->buffer_size, 01679 "HTTP/1.0 200 RTSP Redirect follows\r\n" 01680 /* XXX: incorrect mime type ? */ 01681 "Content-type: application/x-rtsp\r\n" 01682 "\r\n" 01683 "rtsp://%s:%d/%s\r\n", hostname, ntohs(my_rtsp_addr.sin_port), filename); 01684 } 01685 break; 01686 case REDIR_SDP: 01687 { 01688 uint8_t *sdp_data; 01689 int sdp_data_size, len; 01690 struct sockaddr_in my_addr; 01691 01692 q += snprintf(q, c->buffer_size, 01693 "HTTP/1.0 200 OK\r\n" 01694 "Content-type: application/sdp\r\n" 01695 "\r\n"); 01696 01697 len = sizeof(my_addr); 01698 getsockname(c->fd, (struct sockaddr *)&my_addr, &len); 01699 01700 /* XXX: should use a dynamic buffer */ 01701 sdp_data_size = prepare_sdp_description(stream, 01702 &sdp_data, 01703 my_addr.sin_addr); 01704 if (sdp_data_size > 0) { 01705 memcpy(q, sdp_data, sdp_data_size); 01706 q += sdp_data_size; 01707 *q = '\0'; 01708 av_free(sdp_data); 01709 } 01710 } 01711 break; 01712 default: 01713 abort(); 01714 break; 01715 } 01716 01717 /* prepare output buffer */ 01718 c->buffer_ptr = c->buffer; 01719 c->buffer_end = q; 01720 c->state = HTTPSTATE_SEND_HEADER; 01721 return 0; 01722 } 01723 } 01724 } 01725 01726 snprintf(msg, sizeof(msg), "ASX/RAM file not handled"); 01727 goto send_error; 01728 } 01729 01730 stream->conns_served++; 01731 01732 /* XXX: add there authenticate and IP match */ 01733 01734 if (c->post) { 01735 /* if post, it means a feed is being sent */ 01736 if (!stream->is_feed) { 01737 /* However it might be a status report from WMP! Let us log the 01738 * data as it might come in handy one day. */ 01739 char *logline = 0; 01740 int client_id = 0; 01741 01742 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) { 01743 if (strncasecmp(p, "Pragma: log-line=", 17) == 0) { 01744 logline = p; 01745 break; 01746 } 01747 if (strncasecmp(p, "Pragma: client-id=", 18) == 0) 01748 client_id = strtol(p + 18, 0, 10); 01749 p = strchr(p, '\n'); 01750 if (!p) 01751 break; 01752 01753 p++; 01754 } 01755 01756 if (logline) { 01757 char *eol = strchr(logline, '\n'); 01758 01759 logline += 17; 01760 01761 if (eol) { 01762 if (eol[-1] == '\r') 01763 eol--; 01764 http_log("%.*s\n", (int) (eol - logline), logline); 01765 c->suppress_log = 1; 01766 } 01767 } 01768 01769 #ifdef DEBUG_WMP 01770 http_log("\nGot request:\n%s\n", c->buffer); 01771 #endif 01772 01773 if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) { 01774 HTTPContext *wmpc; 01775 01776 /* Now we have to find the client_id */ 01777 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) { 01778 if (wmpc->wmp_client_id == client_id) 01779 break; 01780 } 01781 01782 if (wmpc && modify_current_stream(wmpc, ratebuf)) 01783 wmpc->switch_pending = 1; 01784 } 01785 01786 snprintf(msg, sizeof(msg), "POST command not handled"); 01787 c->stream = 0; 01788 goto send_error; 01789 } 01790 if (http_start_receive_data(c) < 0) { 01791 snprintf(msg, sizeof(msg), "could not open feed"); 01792 goto send_error; 01793 } 01794 c->http_error = 0; 01795 c->state = HTTPSTATE_RECEIVE_DATA; 01796 return 0; 01797 } 01798 01799 #ifdef DEBUG_WMP 01800 if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0) 01801 http_log("\nGot request:\n%s\n", c->buffer); 01802 #endif 01803 01804 if (c->stream->stream_type == STREAM_TYPE_STATUS) 01805 goto send_status; 01806 01807 /* open input stream */ 01808 if (open_input_stream(c, info) < 0) { 01809 snprintf(msg, sizeof(msg), "Input stream corresponding to '%s' not found", url); 01810 goto send_error; 01811 } 01812 01813 /* prepare http header */ 01814 q = c->buffer; 01815 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n"); 01816 mime_type = c->stream->fmt->mime_type; 01817 if (!mime_type) 01818 mime_type = "application/x-octet-stream"; 01819 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Pragma: no-cache\r\n"); 01820 01821 /* for asf, we need extra headers */ 01822 if (!strcmp(c->stream->fmt->name,"asf_stream")) { 01823 /* Need to allocate a client id */ 01824 01825 c->wmp_client_id = av_lfg_get(&random_state); 01826 01827 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Server: Cougar 4.1.0.3923\r\nCache-Control: no-cache\r\nPragma: client-id=%d\r\nPragma: features=\"broadcast\"\r\n", c->wmp_client_id); 01828 } 01829 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-Type: %s\r\n", mime_type); 01830 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n"); 01831 01832 /* prepare output buffer */ 01833 c->http_error = 0; 01834 c->buffer_ptr = c->buffer; 01835 c->buffer_end = q; 01836 c->state = HTTPSTATE_SEND_HEADER; 01837 return 0; 01838 send_error: 01839 c->http_error = 404; 01840 q = c->buffer; 01841 q += snprintf(q, c->buffer_size, 01842 "HTTP/1.0 404 Not Found\r\n" 01843 "Content-type: text/html\r\n" 01844 "\r\n" 01845 "<html>\n" 01846 "<head><title>404 Not Found</title></head>\n" 01847 "<body>%s</body>\n" 01848 "</html>\n", msg); 01849 /* prepare output buffer */ 01850 c->buffer_ptr = c->buffer; 01851 c->buffer_end = q; 01852 c->state = HTTPSTATE_SEND_HEADER; 01853 return 0; 01854 send_status: 01855 compute_status(c); 01856 c->http_error = 200; /* horrible : we use this value to avoid 01857 going to the send data state */ 01858 c->state = HTTPSTATE_SEND_HEADER; 01859 return 0; 01860 } 01861 01862 static void fmt_bytecount(ByteIOContext *pb, int64_t count) 01863 { 01864 static const char *suffix = " kMGTP"; 01865 const char *s; 01866 01867 for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++); 01868 01869 url_fprintf(pb, "%"PRId64"%c", count, *s); 01870 } 01871 01872 static void compute_status(HTTPContext *c) 01873 { 01874 HTTPContext *c1; 01875 FFStream *stream; 01876 char *p; 01877 time_t ti; 01878 int i, len; 01879 ByteIOContext *pb; 01880 01881 if (url_open_dyn_buf(&pb) < 0) { 01882 /* XXX: return an error ? */ 01883 c->buffer_ptr = c->buffer; 01884 c->buffer_end = c->buffer; 01885 return; 01886 } 01887 01888 url_fprintf(pb, "HTTP/1.0 200 OK\r\n"); 01889 url_fprintf(pb, "Content-type: %s\r\n", "text/html"); 01890 url_fprintf(pb, "Pragma: no-cache\r\n"); 01891 url_fprintf(pb, "\r\n"); 01892 01893 url_fprintf(pb, "<html><head><title>%s Status</title>\n", program_name); 01894 if (c->stream->feed_filename[0]) 01895 url_fprintf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename); 01896 url_fprintf(pb, "</head>\n<body>"); 01897 url_fprintf(pb, "<h1>%s Status</h1>\n", program_name); 01898 /* format status */ 01899 url_fprintf(pb, "<h2>Available Streams</h2>\n"); 01900 url_fprintf(pb, "<table cellspacing=0 cellpadding=4>\n"); 01901 url_fprintf(pb, "<tr><th valign=top>Path<th align=left>Served<br>Conns<th><br>bytes<th valign=top>Format<th>Bit rate<br>kbits/s<th align=left>Video<br>kbits/s<th><br>Codec<th align=left>Audio<br>kbits/s<th><br>Codec<th align=left valign=top>Feed\n"); 01902 stream = first_stream; 01903 while (stream != NULL) { 01904 char sfilename[1024]; 01905 char *eosf; 01906 01907 if (stream->feed != stream) { 01908 av_strlcpy(sfilename, stream->filename, sizeof(sfilename) - 10); 01909 eosf = sfilename + strlen(sfilename); 01910 if (eosf - sfilename >= 4) { 01911 if (strcmp(eosf - 4, ".asf") == 0) 01912 strcpy(eosf - 4, ".asx"); 01913 else if (strcmp(eosf - 3, ".rm") == 0) 01914 strcpy(eosf - 3, ".ram"); 01915 else if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) { 01916 /* generate a sample RTSP director if 01917 unicast. Generate an SDP redirector if 01918 multicast */ 01919 eosf = strrchr(sfilename, '.'); 01920 if (!eosf) 01921 eosf = sfilename + strlen(sfilename); 01922 if (stream->is_multicast) 01923 strcpy(eosf, ".sdp"); 01924 else 01925 strcpy(eosf, ".rtsp"); 01926 } 01927 } 01928 01929 url_fprintf(pb, "<tr><td><a href=\"/%s\">%s</a> ", 01930 sfilename, stream->filename); 01931 url_fprintf(pb, "<td align=right> %d <td align=right> ", 01932 stream->conns_served); 01933 fmt_bytecount(pb, stream->bytes_served); 01934 switch(stream->stream_type) { 01935 case STREAM_TYPE_LIVE: { 01936 int audio_bit_rate = 0; 01937 int video_bit_rate = 0; 01938 const char *audio_codec_name = ""; 01939 const char *video_codec_name = ""; 01940 const char *audio_codec_name_extra = ""; 01941 const char *video_codec_name_extra = ""; 01942 01943 for(i=0;i<stream->nb_streams;i++) { 01944 AVStream *st = stream->streams[i]; 01945 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id); 01946 switch(st->codec->codec_type) { 01947 case AVMEDIA_TYPE_AUDIO: 01948 audio_bit_rate += st->codec->bit_rate; 01949 if (codec) { 01950 if (*audio_codec_name) 01951 audio_codec_name_extra = "..."; 01952 audio_codec_name = codec->name; 01953 } 01954 break; 01955 case AVMEDIA_TYPE_VIDEO: 01956 video_bit_rate += st->codec->bit_rate; 01957 if (codec) { 01958 if (*video_codec_name) 01959 video_codec_name_extra = "..."; 01960 video_codec_name = codec->name; 01961 } 01962 break; 01963 case AVMEDIA_TYPE_DATA: 01964 video_bit_rate += st->codec->bit_rate; 01965 break; 01966 default: 01967 abort(); 01968 } 01969 } 01970 url_fprintf(pb, "<td align=center> %s <td align=right> %d <td align=right> %d <td> %s %s <td align=right> %d <td> %s %s", 01971 stream->fmt->name, 01972 stream->bandwidth, 01973 video_bit_rate / 1000, video_codec_name, video_codec_name_extra, 01974 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra); 01975 if (stream->feed) 01976 url_fprintf(pb, "<td>%s", stream->feed->filename); 01977 else 01978 url_fprintf(pb, "<td>%s", stream->feed_filename); 01979 url_fprintf(pb, "\n"); 01980 } 01981 break; 01982 default: 01983 url_fprintf(pb, "<td align=center> - <td align=right> - <td align=right> - <td><td align=right> - <td>\n"); 01984 break; 01985 } 01986 } 01987 stream = stream->next; 01988 } 01989 url_fprintf(pb, "</table>\n"); 01990 01991 stream = first_stream; 01992 while (stream != NULL) { 01993 if (stream->feed == stream) { 01994 url_fprintf(pb, "<h2>Feed %s</h2>", stream->filename); 01995 if (stream->pid) { 01996 url_fprintf(pb, "Running as pid %d.\n", stream->pid); 01997 01998 #if defined(linux) && !defined(CONFIG_NOCUTILS) 01999 { 02000 FILE *pid_stat; 02001 char ps_cmd[64]; 02002 02003 /* This is somewhat linux specific I guess */ 02004 snprintf(ps_cmd, sizeof(ps_cmd), 02005 "ps -o \"%%cpu,cputime\" --no-headers %d", 02006 stream->pid); 02007 02008 pid_stat = popen(ps_cmd, "r"); 02009 if (pid_stat) { 02010 char cpuperc[10]; 02011 char cpuused[64]; 02012 02013 if (fscanf(pid_stat, "%10s %64s", cpuperc, 02014 cpuused) == 2) { 02015 url_fprintf(pb, "Currently using %s%% of the cpu. Total time used %s.\n", 02016 cpuperc, cpuused); 02017 } 02018 fclose(pid_stat); 02019 } 02020 } 02021 #endif 02022 02023 url_fprintf(pb, "<p>"); 02024 } 02025 url_fprintf(pb, "<table cellspacing=0 cellpadding=4><tr><th>Stream<th>type<th>kbits/s<th align=left>codec<th align=left>Parameters\n"); 02026 02027 for (i = 0; i < stream->nb_streams; i++) { 02028 AVStream *st = stream->streams[i]; 02029 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id); 02030 const char *type = "unknown"; 02031 char parameters[64]; 02032 02033 parameters[0] = 0; 02034 02035 switch(st->codec->codec_type) { 02036 case AVMEDIA_TYPE_AUDIO: 02037 type = "audio"; 02038 snprintf(parameters, sizeof(parameters), "%d channel(s), %d Hz", st->codec->channels, st->codec->sample_rate); 02039 break; 02040 case AVMEDIA_TYPE_VIDEO: 02041 type = "video"; 02042 snprintf(parameters, sizeof(parameters), "%dx%d, q=%d-%d, fps=%d", st->codec->width, st->codec->height, 02043 st->codec->qmin, st->codec->qmax, st->codec->time_base.den / st->codec->time_base.num); 02044 break; 02045 default: 02046 abort(); 02047 } 02048 url_fprintf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n", 02049 i, type, st->codec->bit_rate/1000, codec ? codec->name : "", parameters); 02050 } 02051 url_fprintf(pb, "</table>\n"); 02052 02053 } 02054 stream = stream->next; 02055 } 02056 02057 /* connection status */ 02058 url_fprintf(pb, "<h2>Connection Status</h2>\n"); 02059 02060 url_fprintf(pb, "Number of connections: %d / %d<br>\n", 02061 nb_connections, nb_max_connections); 02062 02063 url_fprintf(pb, "Bandwidth in use: %"PRIu64"k / %"PRIu64"k<br>\n", 02064 current_bandwidth, max_bandwidth); 02065 02066 url_fprintf(pb, "<table>\n"); 02067 url_fprintf(pb, "<tr><th>#<th>File<th>IP<th>Proto<th>State<th>Target bits/sec<th>Actual bits/sec<th>Bytes transferred\n"); 02068 c1 = first_http_ctx; 02069 i = 0; 02070 while (c1 != NULL) { 02071 int bitrate; 02072 int j; 02073 02074 bitrate = 0; 02075 if (c1->stream) { 02076 for (j = 0; j < c1->stream->nb_streams; j++) { 02077 if (!c1->stream->feed) 02078 bitrate += c1->stream->streams[j]->codec->bit_rate; 02079 else if (c1->feed_streams[j] >= 0) 02080 bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec->bit_rate; 02081 } 02082 } 02083 02084 i++; 02085 p = inet_ntoa(c1->from_addr.sin_addr); 02086 url_fprintf(pb, "<tr><td><b>%d</b><td>%s%s<td>%s<td>%s<td>%s<td align=right>", 02087 i, 02088 c1->stream ? c1->stream->filename : "", 02089 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "", 02090 p, 02091 c1->protocol, 02092 http_state[c1->state]); 02093 fmt_bytecount(pb, bitrate); 02094 url_fprintf(pb, "<td align=right>"); 02095 fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8); 02096 url_fprintf(pb, "<td align=right>"); 02097 fmt_bytecount(pb, c1->data_count); 02098 url_fprintf(pb, "\n"); 02099 c1 = c1->next; 02100 } 02101 url_fprintf(pb, "</table>\n"); 02102 02103 /* date */ 02104 ti = time(NULL); 02105 p = ctime(&ti); 02106 url_fprintf(pb, "<hr size=1 noshade>Generated at %s", p); 02107 url_fprintf(pb, "</body>\n</html>\n"); 02108 02109 len = url_close_dyn_buf(pb, &c->pb_buffer); 02110 c->buffer_ptr = c->pb_buffer; 02111 c->buffer_end = c->pb_buffer + len; 02112 } 02113 02114 /* check if the parser needs to be opened for stream i */ 02115 static void open_parser(AVFormatContext *s, int i) 02116 { 02117 AVStream *st = s->streams[i]; 02118 AVCodec *codec; 02119 02120 if (!st->codec->codec) { 02121 codec = avcodec_find_decoder(st->codec->codec_id); 02122 if (codec && (codec->capabilities & CODEC_CAP_PARSE_ONLY)) { 02123 st->codec->parse_only = 1; 02124 if (avcodec_open(st->codec, codec) < 0) 02125 st->codec->parse_only = 0; 02126 } 02127 } 02128 } 02129 02130 static int open_input_stream(HTTPContext *c, const char *info) 02131 { 02132 char buf[128]; 02133 char input_filename[1024]; 02134 AVFormatContext *s; 02135 int buf_size, i, ret; 02136 int64_t stream_pos; 02137 02138 /* find file name */ 02139 if (c->stream->feed) { 02140 strcpy(input_filename, c->stream->feed->feed_filename); 02141 buf_size = FFM_PACKET_SIZE; 02142 /* compute position (absolute time) */ 02143 if (find_info_tag(buf, sizeof(buf), "date", info)) { 02144 stream_pos = parse_date(buf, 0); 02145 if (stream_pos == INT64_MIN) 02146 return -1; 02147 } else if (find_info_tag(buf, sizeof(buf), "buffer", info)) { 02148 int prebuffer = strtol(buf, 0, 10); 02149 stream_pos = av_gettime() - prebuffer * (int64_t)1000000; 02150 } else 02151 stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000; 02152 } else { 02153 strcpy(input_filename, c->stream->feed_filename); 02154 buf_size = 0; 02155 /* compute position (relative time) */ 02156 if (find_info_tag(buf, sizeof(buf), "date", info)) { 02157 stream_pos = parse_date(buf, 1); 02158 if (stream_pos == INT64_MIN) 02159 return -1; 02160 } else 02161 stream_pos = 0; 02162 } 02163 if (input_filename[0] == '\0') 02164 return -1; 02165 02166 /* open stream */ 02167 if ((ret = av_open_input_file(&s, input_filename, c->stream->ifmt, 02168 buf_size, c->stream->ap_in)) < 0) { 02169 http_log("could not open %s: %d\n", input_filename, ret); 02170 return -1; 02171 } 02172 s->flags |= AVFMT_FLAG_GENPTS; 02173 c->fmt_in = s; 02174 if (strcmp(s->iformat->name, "ffm") && av_find_stream_info(c->fmt_in) < 0) { 02175 http_log("Could not find stream info '%s'\n", input_filename); 02176 av_close_input_file(s); 02177 return -1; 02178 } 02179 02180 /* open each parser */ 02181 for(i=0;i<s->nb_streams;i++) 02182 open_parser(s, i); 02183 02184 /* choose stream as clock source (we favorize video stream if 02185 present) for packet sending */ 02186 c->pts_stream_index = 0; 02187 for(i=0;i<c->stream->nb_streams;i++) { 02188 if (c->pts_stream_index == 0 && 02189 c->stream->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { 02190 c->pts_stream_index = i; 02191 } 02192 } 02193 02194 #if 1 02195 if (c->fmt_in->iformat->read_seek) 02196 av_seek_frame(c->fmt_in, -1, stream_pos, 0); 02197 #endif 02198 /* set the start time (needed for maxtime and RTP packet timing) */ 02199 c->start_time = cur_time; 02200 c->first_pts = AV_NOPTS_VALUE; 02201 return 0; 02202 } 02203 02204 /* return the server clock (in us) */ 02205 static int64_t get_server_clock(HTTPContext *c) 02206 { 02207 /* compute current pts value from system time */ 02208 return (cur_time - c->start_time) * 1000; 02209 } 02210 02211 /* return the estimated time at which the current packet must be sent 02212 (in us) */ 02213 static int64_t get_packet_send_clock(HTTPContext *c) 02214 { 02215 int bytes_left, bytes_sent, frame_bytes; 02216 02217 frame_bytes = c->cur_frame_bytes; 02218 if (frame_bytes <= 0) 02219 return c->cur_pts; 02220 else { 02221 bytes_left = c->buffer_end - c->buffer_ptr; 02222 bytes_sent = frame_bytes - bytes_left; 02223 return c->cur_pts + (c->cur_frame_duration * bytes_sent) / frame_bytes; 02224 } 02225 } 02226 02227 02228 static int http_prepare_data(HTTPContext *c) 02229 { 02230 int i, len, ret; 02231 AVFormatContext *ctx; 02232 02233 av_freep(&c->pb_buffer); 02234 switch(c->state) { 02235 case HTTPSTATE_SEND_DATA_HEADER: 02236 memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx)); 02237 av_metadata_set2(&c->fmt_ctx.metadata, "author" , c->stream->author , 0); 02238 av_metadata_set2(&c->fmt_ctx.metadata, "comment" , c->stream->comment , 0); 02239 av_metadata_set2(&c->fmt_ctx.metadata, "copyright", c->stream->copyright, 0); 02240 av_metadata_set2(&c->fmt_ctx.metadata, "title" , c->stream->title , 0); 02241 02242 for(i=0;i<c->stream->nb_streams;i++) { 02243 AVStream *st; 02244 AVStream *src; 02245 st = av_mallocz(sizeof(AVStream)); 02246 c->fmt_ctx.streams[i] = st; 02247 /* if file or feed, then just take streams from FFStream struct */ 02248 if (!c->stream->feed || 02249 c->stream->feed == c->stream) 02250 src = c->stream->streams[i]; 02251 else 02252 src = c->stream->feed->streams[c->stream->feed_streams[i]]; 02253 02254 *st = *src; 02255 st->priv_data = 0; 02256 st->codec->frame_number = 0; /* XXX: should be done in 02257 AVStream, not in codec */ 02258 } 02259 /* set output format parameters */ 02260 c->fmt_ctx.oformat = c->stream->fmt; 02261 c->fmt_ctx.nb_streams = c->stream->nb_streams; 02262 02263 c->got_key_frame = 0; 02264 02265 /* prepare header and save header data in a stream */ 02266 if (url_open_dyn_buf(&c->fmt_ctx.pb) < 0) { 02267 /* XXX: potential leak */ 02268 return -1; 02269 } 02270 c->fmt_ctx.pb->is_streamed = 1; 02271 02272 /* 02273 * HACK to avoid mpeg ps muxer to spit many underflow errors 02274 * Default value from FFmpeg 02275 * Try to set it use configuration option 02276 */ 02277 c->fmt_ctx.preload = (int)(0.5*AV_TIME_BASE); 02278 c->fmt_ctx.max_delay = (int)(0.7*AV_TIME_BASE); 02279 02280 av_set_parameters(&c->fmt_ctx, NULL); 02281 if (av_write_header(&c->fmt_ctx) < 0) { 02282 http_log("Error writing output header\n"); 02283 return -1; 02284 } 02285 av_metadata_free(&c->fmt_ctx.metadata); 02286 02287 len = url_close_dyn_buf(c->fmt_ctx.pb, &c->pb_buffer); 02288 c->buffer_ptr = c->pb_buffer; 02289 c->buffer_end = c->pb_buffer + len; 02290 02291 c->state = HTTPSTATE_SEND_DATA; 02292 c->last_packet_sent = 0; 02293 break; 02294 case HTTPSTATE_SEND_DATA: 02295 /* find a new packet */ 02296 /* read a packet from the input stream */ 02297 if (c->stream->feed) 02298 ffm_set_write_index(c->fmt_in, 02299 c->stream->feed->feed_write_index, 02300 c->stream->feed->feed_size); 02301 02302 if (c->stream->max_time && 02303 c->stream->max_time + c->start_time - cur_time < 0) 02304 /* We have timed out */ 02305 c->state = HTTPSTATE_SEND_DATA_TRAILER; 02306 else { 02307 AVPacket pkt; 02308 redo: 02309 if (av_read_frame(c->fmt_in, &pkt) < 0) { 02310 if (c->stream->feed && c->stream->feed->feed_opened) { 02311 /* if coming from feed, it means we reached the end of the 02312 ffm file, so must wait for more data */ 02313 c->state = HTTPSTATE_WAIT_FEED; 02314 return 1; /* state changed */ 02315 } else { 02316 if (c->stream->loop) { 02317 av_close_input_file(c->fmt_in); 02318 c->fmt_in = NULL; 02319 if (open_input_stream(c, "") < 0) 02320 goto no_loop; 02321 goto redo; 02322 } else { 02323 no_loop: 02324 /* must send trailer now because eof or error */ 02325 c->state = HTTPSTATE_SEND_DATA_TRAILER; 02326 } 02327 } 02328 } else { 02329 int source_index = pkt.stream_index; 02330 /* update first pts if needed */ 02331 if (c->first_pts == AV_NOPTS_VALUE) { 02332 c->first_pts = av_rescale_q(pkt.dts, c->fmt_in->streams[pkt.stream_index]->time_base, AV_TIME_BASE_Q); 02333 c->start_time = cur_time; 02334 } 02335 /* send it to the appropriate stream */ 02336 if (c->stream->feed) { 02337 /* if coming from a feed, select the right stream */ 02338 if (c->switch_pending) { 02339 c->switch_pending = 0; 02340 for(i=0;i<c->stream->nb_streams;i++) { 02341 if (c->switch_feed_streams[i] == pkt.stream_index) 02342 if (pkt.flags & AV_PKT_FLAG_KEY) 02343 do_switch_stream(c, i); 02344 if (c->switch_feed_streams[i] >= 0) 02345 c->switch_pending = 1; 02346 } 02347 } 02348 for(i=0;i<c->stream->nb_streams;i++) { 02349 if (c->stream->feed_streams[i] == pkt.stream_index) { 02350 AVStream *st = c->fmt_in->streams[source_index]; 02351 pkt.stream_index = i; 02352 if (pkt.flags & AV_PKT_FLAG_KEY && 02353 (st->codec->codec_type == AVMEDIA_TYPE_VIDEO || 02354 c->stream->nb_streams == 1)) 02355 c->got_key_frame = 1; 02356 if (!c->stream->send_on_key || c->got_key_frame) 02357 goto send_it; 02358 } 02359 } 02360 } else { 02361 AVCodecContext *codec; 02362 AVStream *ist, *ost; 02363 send_it: 02364 ist = c->fmt_in->streams[source_index]; 02365 /* specific handling for RTP: we use several 02366 output stream (one for each RTP 02367 connection). XXX: need more abstract handling */ 02368 if (c->is_packetized) { 02369 /* compute send time and duration */ 02370 c->cur_pts = av_rescale_q(pkt.dts, ist->time_base, AV_TIME_BASE_Q); 02371 if (ist->start_time != AV_NOPTS_VALUE) 02372 c->cur_pts -= av_rescale_q(ist->start_time, ist->time_base, AV_TIME_BASE_Q); 02373 c->cur_frame_duration = av_rescale_q(pkt.duration, ist->time_base, AV_TIME_BASE_Q); 02374 /* find RTP context */ 02375 c->packet_stream_index = pkt.stream_index; 02376 ctx = c->rtp_ctx[c->packet_stream_index]; 02377 if(!ctx) { 02378 av_free_packet(&pkt); 02379 break; 02380 } 02381 codec = ctx->streams[0]->codec; 02382 /* only one stream per RTP connection */ 02383 pkt.stream_index = 0; 02384 } else { 02385 ctx = &c->fmt_ctx; 02386 /* Fudge here */ 02387 codec = ctx->streams[pkt.stream_index]->codec; 02388 } 02389 02390 if (c->is_packetized) { 02391 int max_packet_size; 02392 if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP) 02393 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE; 02394 else 02395 max_packet_size = url_get_max_packet_size(c->rtp_handles[c->packet_stream_index]); 02396 ret = url_open_dyn_packet_buf(&ctx->pb, max_packet_size); 02397 } else { 02398 ret = url_open_dyn_buf(&ctx->pb); 02399 } 02400 if (ret < 0) { 02401 /* XXX: potential leak */ 02402 return -1; 02403 } 02404 ost = ctx->streams[pkt.stream_index]; 02405 02406 ctx->pb->is_streamed = 1; 02407 if (pkt.dts != AV_NOPTS_VALUE) 02408 pkt.dts = av_rescale_q(pkt.dts, ist->time_base, ost->time_base); 02409 if (pkt.pts != AV_NOPTS_VALUE) 02410 pkt.pts = av_rescale_q(pkt.pts, ist->time_base, ost->time_base); 02411 pkt.duration = av_rescale_q(pkt.duration, ist->time_base, ost->time_base); 02412 if (av_write_frame(ctx, &pkt) < 0) { 02413 http_log("Error writing frame to output\n"); 02414 c->state = HTTPSTATE_SEND_DATA_TRAILER; 02415 } 02416 02417 len = url_close_dyn_buf(ctx->pb, &c->pb_buffer); 02418 c->cur_frame_bytes = len; 02419 c->buffer_ptr = c->pb_buffer; 02420 c->buffer_end = c->pb_buffer + len; 02421 02422 codec->frame_number++; 02423 if (len == 0) { 02424 av_free_packet(&pkt); 02425 goto redo; 02426 } 02427 } 02428 av_free_packet(&pkt); 02429 } 02430 } 02431 break; 02432 default: 02433 case HTTPSTATE_SEND_DATA_TRAILER: 02434 /* last packet test ? */ 02435 if (c->last_packet_sent || c->is_packetized) 02436 return -1; 02437 ctx = &c->fmt_ctx; 02438 /* prepare header */ 02439 if (url_open_dyn_buf(&ctx->pb) < 0) { 02440 /* XXX: potential leak */ 02441 return -1; 02442 } 02443 c->fmt_ctx.pb->is_streamed = 1; 02444 av_write_trailer(ctx); 02445 len = url_close_dyn_buf(ctx->pb, &c->pb_buffer); 02446 c->buffer_ptr = c->pb_buffer; 02447 c->buffer_end = c->pb_buffer + len; 02448 02449 c->last_packet_sent = 1; 02450 break; 02451 } 02452 return 0; 02453 } 02454 02455 /* should convert the format at the same time */ 02456 /* send data starting at c->buffer_ptr to the output connection 02457 (either UDP or TCP connection) */ 02458 static int http_send_data(HTTPContext *c) 02459 { 02460 int len, ret; 02461 02462 for(;;) { 02463 if (c->buffer_ptr >= c->buffer_end) { 02464 ret = http_prepare_data(c); 02465 if (ret < 0) 02466 return -1; 02467 else if (ret != 0) 02468 /* state change requested */ 02469 break; 02470 } else { 02471 if (c->is_packetized) { 02472 /* RTP data output */ 02473 len = c->buffer_end - c->buffer_ptr; 02474 if (len < 4) { 02475 /* fail safe - should never happen */ 02476 fail1: 02477 c->buffer_ptr = c->buffer_end; 02478 return 0; 02479 } 02480 len = (c->buffer_ptr[0] << 24) | 02481 (c->buffer_ptr[1] << 16) | 02482 (c->buffer_ptr[2] << 8) | 02483 (c->buffer_ptr[3]); 02484 if (len > (c->buffer_end - c->buffer_ptr)) 02485 goto fail1; 02486 if ((get_packet_send_clock(c) - get_server_clock(c)) > 0) { 02487 /* nothing to send yet: we can wait */ 02488 return 0; 02489 } 02490 02491 c->data_count += len; 02492 update_datarate(&c->datarate, c->data_count); 02493 if (c->stream) 02494 c->stream->bytes_served += len; 02495 02496 if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP) { 02497 /* RTP packets are sent inside the RTSP TCP connection */ 02498 ByteIOContext *pb; 02499 int interleaved_index, size; 02500 uint8_t header[4]; 02501 HTTPContext *rtsp_c; 02502 02503 rtsp_c = c->rtsp_c; 02504 /* if no RTSP connection left, error */ 02505 if (!rtsp_c) 02506 return -1; 02507 /* if already sending something, then wait. */ 02508 if (rtsp_c->state != RTSPSTATE_WAIT_REQUEST) 02509 break; 02510 if (url_open_dyn_buf(&pb) < 0) 02511 goto fail1; 02512 interleaved_index = c->packet_stream_index * 2; 02513 /* RTCP packets are sent at odd indexes */ 02514 if (c->buffer_ptr[1] == 200) 02515 interleaved_index++; 02516 /* write RTSP TCP header */ 02517 header[0] = '$'; 02518 header[1] = interleaved_index; 02519 header[2] = len >> 8; 02520 header[3] = len; 02521 put_buffer(pb, header, 4); 02522 /* write RTP packet data */ 02523 c->buffer_ptr += 4; 02524 put_buffer(pb, c->buffer_ptr, len); 02525 size = url_close_dyn_buf(pb, &c->packet_buffer); 02526 /* prepare asynchronous TCP sending */ 02527 rtsp_c->packet_buffer_ptr = c->packet_buffer; 02528 rtsp_c->packet_buffer_end = c->packet_buffer + size; 02529 c->buffer_ptr += len; 02530 02531 /* send everything we can NOW */ 02532 len = send(rtsp_c->fd, rtsp_c->packet_buffer_ptr, 02533 rtsp_c->packet_buffer_end - rtsp_c->packet_buffer_ptr, 0); 02534 if (len > 0) 02535 rtsp_c->packet_buffer_ptr += len; 02536 if (rtsp_c->packet_buffer_ptr < rtsp_c->packet_buffer_end) { 02537 /* if we could not send all the data, we will 02538 send it later, so a new state is needed to 02539 "lock" the RTSP TCP connection */ 02540 rtsp_c->state = RTSPSTATE_SEND_PACKET; 02541 break; 02542 } else 02543 /* all data has been sent */ 02544 av_freep(&c->packet_buffer); 02545 } else { 02546 /* send RTP packet directly in UDP */ 02547 c->buffer_ptr += 4; 02548 url_write(c->rtp_handles[c->packet_stream_index], 02549 c->buffer_ptr, len); 02550 c->buffer_ptr += len; 02551 /* here we continue as we can send several packets per 10 ms slot */ 02552 } 02553 } else { 02554 /* TCP data output */ 02555 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0); 02556 if (len < 0) { 02557 if (ff_neterrno() != FF_NETERROR(EAGAIN) && 02558 ff_neterrno() != FF_NETERROR(EINTR)) 02559 /* error : close connection */ 02560 return -1; 02561 else 02562 return 0; 02563 } else 02564 c->buffer_ptr += len; 02565 02566 c->data_count += len; 02567 update_datarate(&c->datarate, c->data_count); 02568 if (c->stream) 02569 c->stream->bytes_served += len; 02570 break; 02571 } 02572 } 02573 } /* for(;;) */ 02574 return 0; 02575 } 02576 02577 static int http_start_receive_data(HTTPContext *c) 02578 { 02579 int fd; 02580 02581 if (c->stream->feed_opened) 02582 return -1; 02583 02584 /* Don't permit writing to this one */ 02585 if (c->stream->readonly) 02586 return -1; 02587 02588 /* open feed */ 02589 fd = open(c->stream->feed_filename, O_RDWR); 02590 if (fd < 0) { 02591 http_log("Error opening feeder file: %s\n", strerror(errno)); 02592 return -1; 02593 } 02594 c->feed_fd = fd; 02595 02596 if (c->stream->truncate) { 02597 /* truncate feed file */ 02598 ffm_write_write_index(c->feed_fd, FFM_PACKET_SIZE); 02599 ftruncate(c->feed_fd, FFM_PACKET_SIZE); 02600 http_log("Truncating feed file '%s'\n", c->stream->feed_filename); 02601 } else { 02602 if ((c->stream->feed_write_index = ffm_read_write_index(fd)) < 0) { 02603 http_log("Error reading write index from feed file: %s\n", strerror(errno)); 02604 return -1; 02605 } 02606 } 02607 02608 c->stream->feed_write_index = FFMAX(ffm_read_write_index(fd), FFM_PACKET_SIZE); 02609 c->stream->feed_size = lseek(fd, 0, SEEK_END); 02610 lseek(fd, 0, SEEK_SET); 02611 02612 /* init buffer input */ 02613 c->buffer_ptr = c->buffer; 02614 c->buffer_end = c->buffer + FFM_PACKET_SIZE; 02615 c->stream->feed_opened = 1; 02616 c->chunked_encoding = !!av_stristr(c->buffer, "Transfer-Encoding: chunked"); 02617 return 0; 02618 } 02619 02620 static int http_receive_data(HTTPContext *c) 02621 { 02622 HTTPContext *c1; 02623 int len, loop_run = 0; 02624 02625 while (c->chunked_encoding && !c->chunk_size && 02626 c->buffer_end > c->buffer_ptr) { 02627 /* read chunk header, if present */ 02628 len = recv(c->fd, c->buffer_ptr, 1, 0); 02629 02630 if (len < 0) { 02631 if (ff_neterrno() != FF_NETERROR(EAGAIN) && 02632 ff_neterrno() != FF_NETERROR(EINTR)) 02633 /* error : close connection */ 02634 goto fail; 02635 } else if (len == 0) { 02636 /* end of connection : close it */ 02637 goto fail; 02638 } else if (c->buffer_ptr - c->buffer >= 2 && 02639 !memcmp(c->buffer_ptr - 1, "\r\n", 2)) { 02640 c->chunk_size = strtol(c->buffer, 0, 16); 02641 if (c->chunk_size == 0) // end of stream 02642 goto fail; 02643 c->buffer_ptr = c->buffer; 02644 break; 02645 } else if (++loop_run > 10) { 02646 /* no chunk header, abort */ 02647 goto fail; 02648 } else { 02649 c->buffer_ptr++; 02650 } 02651 } 02652 02653 if (c->buffer_end > c->buffer_ptr) { 02654 len = recv(c->fd, c->buffer_ptr, 02655 FFMIN(c->chunk_size, c->buffer_end - c->buffer_ptr), 0); 02656 if (len < 0) { 02657 if (ff_neterrno() != FF_NETERROR(EAGAIN) && 02658 ff_neterrno() != FF_NETERROR(EINTR)) 02659 /* error : close connection */ 02660 goto fail; 02661 } else if (len == 0) 02662 /* end of connection : close it */ 02663 goto fail; 02664 else { 02665 c->chunk_size -= len; 02666 c->buffer_ptr += len; 02667 c->data_count += len; 02668 update_datarate(&c->datarate, c->data_count); 02669 } 02670 } 02671 02672 if (c->buffer_ptr - c->buffer >= 2 && c->data_count > FFM_PACKET_SIZE) { 02673 if (c->buffer[0] != 'f' || 02674 c->buffer[1] != 'm') { 02675 http_log("Feed stream has become desynchronized -- disconnecting\n"); 02676 goto fail; 02677 } 02678 } 02679 02680 if (c->buffer_ptr >= c->buffer_end) { 02681 FFStream *feed = c->stream; 02682 /* a packet has been received : write it in the store, except 02683 if header */ 02684 if (c->data_count > FFM_PACKET_SIZE) { 02685 02686 // printf("writing pos=0x%"PRIx64" size=0x%"PRIx64"\n", feed->feed_write_index, feed->feed_size); 02687 /* XXX: use llseek or url_seek */ 02688 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET); 02689 if (write(c->feed_fd, c->buffer, FFM_PACKET_SIZE) < 0) { 02690 http_log("Error writing to feed file: %s\n", strerror(errno)); 02691 goto fail; 02692 } 02693 02694 feed->feed_write_index += FFM_PACKET_SIZE; 02695 /* update file size */ 02696 if (feed->feed_write_index > c->stream->feed_size) 02697 feed->feed_size = feed->feed_write_index; 02698 02699 /* handle wrap around if max file size reached */ 02700 if (c->stream->feed_max_size && feed->feed_write_index >= c->stream->feed_max_size) 02701 feed->feed_write_index = FFM_PACKET_SIZE; 02702 02703 /* write index */ 02704 if (ffm_write_write_index(c->feed_fd, feed->feed_write_index) < 0) { 02705 http_log("Error writing index to feed file: %s\n", strerror(errno)); 02706 goto fail; 02707 } 02708 02709 /* wake up any waiting connections */ 02710 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) { 02711 if (c1->state == HTTPSTATE_WAIT_FEED && 02712 c1->stream->feed == c->stream->feed) 02713 c1->state = HTTPSTATE_SEND_DATA; 02714 } 02715 } else { 02716 /* We have a header in our hands that contains useful data */ 02717 AVFormatContext *s = NULL; 02718 ByteIOContext *pb; 02719 AVInputFormat *fmt_in; 02720 int i; 02721 02722 /* use feed output format name to find corresponding input format */ 02723 fmt_in = av_find_input_format(feed->fmt->name); 02724 if (!fmt_in) 02725 goto fail; 02726 02727 url_open_buf(&pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY); 02728 pb->is_streamed = 1; 02729 02730 if (av_open_input_stream(&s, pb, c->stream->feed_filename, fmt_in, NULL) < 0) { 02731 av_free(pb); 02732 goto fail; 02733 } 02734 02735 /* Now we have the actual streams */ 02736 if (s->nb_streams != feed->nb_streams) { 02737 av_close_input_stream(s); 02738 av_free(pb); 02739 http_log("Feed '%s' stream number does not match registered feed\n", 02740 c->stream->feed_filename); 02741 goto fail; 02742 } 02743 02744 for (i = 0; i < s->nb_streams; i++) { 02745 AVStream *fst = feed->streams[i]; 02746 AVStream *st = s->streams[i]; 02747 memcpy(fst->codec, st->codec, sizeof(AVCodecContext)); 02748 if (fst->codec->extradata_size) { 02749 fst->codec->extradata = av_malloc(fst->codec->extradata_size); 02750 if (!fst->codec->extradata) 02751 goto fail; 02752 memcpy(fst->codec->extradata, st->codec->extradata, 02753 fst->codec->extradata_size); 02754 } 02755 } 02756 02757 av_close_input_stream(s); 02758 av_free(pb); 02759 } 02760 c->buffer_ptr = c->buffer; 02761 } 02762 02763 return 0; 02764 fail: 02765 c->stream->feed_opened = 0; 02766 close(c->feed_fd); 02767 /* wake up any waiting connections to stop waiting for feed */ 02768 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) { 02769 if (c1->state == HTTPSTATE_WAIT_FEED && 02770 c1->stream->feed == c->stream->feed) 02771 c1->state = HTTPSTATE_SEND_DATA_TRAILER; 02772 } 02773 return -1; 02774 } 02775 02776 /********************************************************************/ 02777 /* RTSP handling */ 02778 02779 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number) 02780 { 02781 const char *str; 02782 time_t ti; 02783 char *p; 02784 char buf2[32]; 02785 02786 switch(error_number) { 02787 case RTSP_STATUS_OK: 02788 str = "OK"; 02789 break; 02790 case RTSP_STATUS_METHOD: 02791 str = "Method Not Allowed"; 02792 break; 02793 case RTSP_STATUS_BANDWIDTH: 02794 str = "Not Enough Bandwidth"; 02795 break; 02796 case RTSP_STATUS_SESSION: 02797 str = "Session Not Found"; 02798 break; 02799 case RTSP_STATUS_STATE: 02800 str = "Method Not Valid in This State"; 02801 break; 02802 case RTSP_STATUS_AGGREGATE: 02803 str = "Aggregate operation not allowed"; 02804 break; 02805 case RTSP_STATUS_ONLY_AGGREGATE: 02806 str = "Only aggregate operation allowed"; 02807 break; 02808 case RTSP_STATUS_TRANSPORT: 02809 str = "Unsupported transport"; 02810 break; 02811 case RTSP_STATUS_INTERNAL: 02812 str = "Internal Server Error"; 02813 break; 02814 case RTSP_STATUS_SERVICE: 02815 str = "Service Unavailable"; 02816 break; 02817 case RTSP_STATUS_VERSION: 02818 str = "RTSP Version not supported"; 02819 break; 02820 default: 02821 str = "Unknown Error"; 02822 break; 02823 } 02824 02825 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str); 02826 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq); 02827 02828 /* output GMT time */ 02829 ti = time(NULL); 02830 p = ctime(&ti); 02831 strcpy(buf2, p); 02832 p = buf2 + strlen(p) - 1; 02833 if (*p == '\n') 02834 *p = '\0'; 02835 url_fprintf(c->pb, "Date: %s GMT\r\n", buf2); 02836 } 02837 02838 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number) 02839 { 02840 rtsp_reply_header(c, error_number); 02841 url_fprintf(c->pb, "\r\n"); 02842 } 02843 02844 static int rtsp_parse_request(HTTPContext *c) 02845 { 02846 const char *p, *p1, *p2; 02847 char cmd[32]; 02848 char url[1024]; 02849 char protocol[32]; 02850 char line[1024]; 02851 int len; 02852 RTSPMessageHeader header1, *header = &header1; 02853 02854 c->buffer_ptr[0] = '\0'; 02855 p = c->buffer; 02856 02857 get_word(cmd, sizeof(cmd), &p); 02858 get_word(url, sizeof(url), &p); 02859 get_word(protocol, sizeof(protocol), &p); 02860 02861 av_strlcpy(c->method, cmd, sizeof(c->method)); 02862 av_strlcpy(c->url, url, sizeof(c->url)); 02863 av_strlcpy(c->protocol, protocol, sizeof(c->protocol)); 02864 02865 if (url_open_dyn_buf(&c->pb) < 0) { 02866 /* XXX: cannot do more */ 02867 c->pb = NULL; /* safety */ 02868 return -1; 02869 } 02870 02871 /* check version name */ 02872 if (strcmp(protocol, "RTSP/1.0") != 0) { 02873 rtsp_reply_error(c, RTSP_STATUS_VERSION); 02874 goto the_end; 02875 } 02876 02877 /* parse each header line */ 02878 memset(header, 0, sizeof(*header)); 02879 /* skip to next line */ 02880 while (*p != '\n' && *p != '\0') 02881 p++; 02882 if (*p == '\n') 02883 p++; 02884 while (*p != '\0') { 02885 p1 = memchr(p, '\n', (char *)c->buffer_ptr - p); 02886 if (!p1) 02887 break; 02888 p2 = p1; 02889 if (p2 > p && p2[-1] == '\r') 02890 p2--; 02891 /* skip empty line */ 02892 if (p2 == p) 02893 break; 02894 len = p2 - p; 02895 if (len > sizeof(line) - 1) 02896 len = sizeof(line) - 1; 02897 memcpy(line, p, len); 02898 line[len] = '\0'; 02899 ff_rtsp_parse_line(header, line, NULL); 02900 p = p1 + 1; 02901 } 02902 02903 /* handle sequence number */ 02904 c->seq = header->seq; 02905 02906 if (!strcmp(cmd, "DESCRIBE")) 02907 rtsp_cmd_describe(c, url); 02908 else if (!strcmp(cmd, "OPTIONS")) 02909 rtsp_cmd_options(c, url); 02910 else if (!strcmp(cmd, "SETUP")) 02911 rtsp_cmd_setup(c, url, header); 02912 else if (!strcmp(cmd, "PLAY")) 02913 rtsp_cmd_play(c, url, header); 02914 else if (!strcmp(cmd, "PAUSE")) 02915 rtsp_cmd_pause(c, url, header); 02916 else if (!strcmp(cmd, "TEARDOWN")) 02917 rtsp_cmd_teardown(c, url, header); 02918 else 02919 rtsp_reply_error(c, RTSP_STATUS_METHOD); 02920 02921 the_end: 02922 len = url_close_dyn_buf(c->pb, &c->pb_buffer); 02923 c->pb = NULL; /* safety */ 02924 if (len < 0) { 02925 /* XXX: cannot do more */ 02926 return -1; 02927 } 02928 c->buffer_ptr = c->pb_buffer; 02929 c->buffer_end = c->pb_buffer + len; 02930 c->state = RTSPSTATE_SEND_REPLY; 02931 return 0; 02932 } 02933 02934 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer, 02935 struct in_addr my_ip) 02936 { 02937 AVFormatContext *avc; 02938 AVStream avs[MAX_STREAMS]; 02939 int i; 02940 02941 avc = avformat_alloc_context(); 02942 if (avc == NULL) { 02943 return -1; 02944 } 02945 av_metadata_set2(&avc->metadata, "title", 02946 stream->title[0] ? stream->title : "No Title", 0); 02947 avc->nb_streams = stream->nb_streams; 02948 if (stream->is_multicast) { 02949 snprintf(avc->filename, 1024, "rtp://%s:%d?multicast=1?ttl=%d", 02950 inet_ntoa(stream->multicast_ip), 02951 stream->multicast_port, stream->multicast_ttl); 02952 } else { 02953 snprintf(avc->filename, 1024, "rtp://0.0.0.0"); 02954 } 02955 02956 for(i = 0; i < stream->nb_streams; i++) { 02957 avc->streams[i] = &avs[i]; 02958 avc->streams[i]->codec = stream->streams[i]->codec; 02959 } 02960 *pbuffer = av_mallocz(2048); 02961 avf_sdp_create(&avc, 1, *pbuffer, 2048); 02962 av_metadata_free(&avc->metadata); 02963 av_free(avc); 02964 02965 return strlen(*pbuffer); 02966 } 02967 02968 static void rtsp_cmd_options(HTTPContext *c, const char *url) 02969 { 02970 // rtsp_reply_header(c, RTSP_STATUS_OK); 02971 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK"); 02972 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq); 02973 url_fprintf(c->pb, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE"); 02974 url_fprintf(c->pb, "\r\n"); 02975 } 02976 02977 static void rtsp_cmd_describe(HTTPContext *c, const char *url) 02978 { 02979 FFStream *stream; 02980 char path1[1024]; 02981 const char *path; 02982 uint8_t *content; 02983 int content_length, len; 02984 struct sockaddr_in my_addr; 02985 02986 /* find which url is asked */ 02987 ff_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url); 02988 path = path1; 02989 if (*path == '/') 02990 path++; 02991 02992 for(stream = first_stream; stream != NULL; stream = stream->next) { 02993 if (!stream->is_feed && 02994 stream->fmt && !strcmp(stream->fmt->name, "rtp") && 02995 !strcmp(path, stream->filename)) { 02996 goto found; 02997 } 02998 } 02999 /* no stream found */ 03000 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */ 03001 return; 03002 03003 found: 03004 /* prepare the media description in sdp format */ 03005 03006 /* get the host IP */ 03007 len = sizeof(my_addr); 03008 getsockname(c->fd, (struct sockaddr *)&my_addr, &len); 03009 content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr); 03010 if (content_length < 0) { 03011 rtsp_reply_error(c, RTSP_STATUS_INTERNAL); 03012 return; 03013 } 03014 rtsp_reply_header(c, RTSP_STATUS_OK); 03015 url_fprintf(c->pb, "Content-Base: %s/\r\n", url); 03016 url_fprintf(c->pb, "Content-Type: application/sdp\r\n"); 03017 url_fprintf(c->pb, "Content-Length: %d\r\n", content_length); 03018 url_fprintf(c->pb, "\r\n"); 03019 put_buffer(c->pb, content, content_length); 03020 av_free(content); 03021 } 03022 03023 static HTTPContext *find_rtp_session(const char *session_id) 03024 { 03025 HTTPContext *c; 03026 03027 if (session_id[0] == '\0') 03028 return NULL; 03029 03030 for(c = first_http_ctx; c != NULL; c = c->next) { 03031 if (!strcmp(c->session_id, session_id)) 03032 return c; 03033 } 03034 return NULL; 03035 } 03036 03037 static RTSPTransportField *find_transport(RTSPMessageHeader *h, enum RTSPLowerTransport lower_transport) 03038 { 03039 RTSPTransportField *th; 03040 int i; 03041 03042 for(i=0;i<h->nb_transports;i++) { 03043 th = &h->transports[i]; 03044 if (th->lower_transport == lower_transport) 03045 return th; 03046 } 03047 return NULL; 03048 } 03049 03050 static void rtsp_cmd_setup(HTTPContext *c, const char *url, 03051 RTSPMessageHeader *h) 03052 { 03053 FFStream *stream; 03054 int stream_index, rtp_port, rtcp_port; 03055 char buf[1024]; 03056 char path1[1024]; 03057 const char *path; 03058 HTTPContext *rtp_c; 03059 RTSPTransportField *th; 03060 struct sockaddr_in dest_addr; 03061 RTSPActionServerSetup setup; 03062 03063 /* find which url is asked */ 03064 ff_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url); 03065 path = path1; 03066 if (*path == '/') 03067 path++; 03068 03069 /* now check each stream */ 03070 for(stream = first_stream; stream != NULL; stream = stream->next) { 03071 if (!stream->is_feed && 03072 stream->fmt && !strcmp(stream->fmt->name, "rtp")) { 03073 /* accept aggregate filenames only if single stream */ 03074 if (!strcmp(path, stream->filename)) { 03075 if (stream->nb_streams != 1) { 03076 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE); 03077 return; 03078 } 03079 stream_index = 0; 03080 goto found; 03081 } 03082 03083 for(stream_index = 0; stream_index < stream->nb_streams; 03084 stream_index++) { 03085 snprintf(buf, sizeof(buf), "%s/streamid=%d", 03086 stream->filename, stream_index); 03087 if (!strcmp(path, buf)) 03088 goto found; 03089 } 03090 } 03091 } 03092 /* no stream found */ 03093 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */ 03094 return; 03095 found: 03096 03097 /* generate session id if needed */ 03098 if (h->session_id[0] == '\0') 03099 snprintf(h->session_id, sizeof(h->session_id), "%08x%08x", 03100 av_lfg_get(&random_state), av_lfg_get(&random_state)); 03101 03102 /* find rtp session, and create it if none found */ 03103 rtp_c = find_rtp_session(h->session_id); 03104 if (!rtp_c) { 03105 /* always prefer UDP */ 03106 th = find_transport(h, RTSP_LOWER_TRANSPORT_UDP); 03107 if (!th) { 03108 th = find_transport(h, RTSP_LOWER_TRANSPORT_TCP); 03109 if (!th) { 03110 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT); 03111 return; 03112 } 03113 } 03114 03115 rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id, 03116 th->lower_transport); 03117 if (!rtp_c) { 03118 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH); 03119 return; 03120 } 03121 03122 /* open input stream */ 03123 if (open_input_stream(rtp_c, "") < 0) { 03124 rtsp_reply_error(c, RTSP_STATUS_INTERNAL); 03125 return; 03126 } 03127 } 03128 03129 /* test if stream is OK (test needed because several SETUP needs 03130 to be done for a given file) */ 03131 if (rtp_c->stream != stream) { 03132 rtsp_reply_error(c, RTSP_STATUS_SERVICE); 03133 return; 03134 } 03135 03136 /* test if stream is already set up */ 03137 if (rtp_c->rtp_ctx[stream_index]) { 03138 rtsp_reply_error(c, RTSP_STATUS_STATE); 03139 return; 03140 } 03141 03142 /* check transport */ 03143 th = find_transport(h, rtp_c->rtp_protocol); 03144 if (!th || (th->lower_transport == RTSP_LOWER_TRANSPORT_UDP && 03145 th->client_port_min <= 0)) { 03146 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT); 03147 return; 03148 } 03149 03150 /* setup default options */ 03151 setup.transport_option[0] = '\0'; 03152 dest_addr = rtp_c->from_addr; 03153 dest_addr.sin_port = htons(th->client_port_min); 03154 03155 /* setup stream */ 03156 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) { 03157 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT); 03158 return; 03159 } 03160 03161 /* now everything is OK, so we can send the connection parameters */ 03162 rtsp_reply_header(c, RTSP_STATUS_OK); 03163 /* session ID */ 03164 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id); 03165 03166 switch(rtp_c->rtp_protocol) { 03167 case RTSP_LOWER_TRANSPORT_UDP: 03168 rtp_port = rtp_get_local_rtp_port(rtp_c->rtp_handles[stream_index]); 03169 rtcp_port = rtp_get_local_rtcp_port(rtp_c->rtp_handles[stream_index]); 03170 url_fprintf(c->pb, "Transport: RTP/AVP/UDP;unicast;" 03171 "client_port=%d-%d;server_port=%d-%d", 03172 th->client_port_min, th->client_port_max, 03173 rtp_port, rtcp_port); 03174 break; 03175 case RTSP_LOWER_TRANSPORT_TCP: 03176 url_fprintf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d", 03177 stream_index * 2, stream_index * 2 + 1); 03178 break; 03179 default: 03180 break; 03181 } 03182 if (setup.transport_option[0] != '\0') 03183 url_fprintf(c->pb, ";%s", setup.transport_option); 03184 url_fprintf(c->pb, "\r\n"); 03185 03186 03187 url_fprintf(c->pb, "\r\n"); 03188 } 03189 03190 03191 /* find an rtp connection by using the session ID. Check consistency 03192 with filename */ 03193 static HTTPContext *find_rtp_session_with_url(const char *url, 03194 const char *session_id) 03195 { 03196 HTTPContext *rtp_c; 03197 char path1[1024]; 03198 const char *path; 03199 char buf[1024]; 03200 int s; 03201 03202 rtp_c = find_rtp_session(session_id); 03203 if (!rtp_c) 03204 return NULL; 03205 03206 /* find which url is asked */ 03207 ff_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url); 03208 path = path1; 03209 if (*path == '/') 03210 path++; 03211 if(!strcmp(path, rtp_c->stream->filename)) return rtp_c; 03212 for(s=0; s<rtp_c->stream->nb_streams; ++s) { 03213 snprintf(buf, sizeof(buf), "%s/streamid=%d", 03214 rtp_c->stream->filename, s); 03215 if(!strncmp(path, buf, sizeof(buf))) { 03216 // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1? 03217 return rtp_c; 03218 } 03219 } 03220 return NULL; 03221 } 03222 03223 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPMessageHeader *h) 03224 { 03225 HTTPContext *rtp_c; 03226 03227 rtp_c = find_rtp_session_with_url(url, h->session_id); 03228 if (!rtp_c) { 03229 rtsp_reply_error(c, RTSP_STATUS_SESSION); 03230 return; 03231 } 03232 03233 if (rtp_c->state != HTTPSTATE_SEND_DATA && 03234 rtp_c->state != HTTPSTATE_WAIT_FEED && 03235 rtp_c->state != HTTPSTATE_READY) { 03236 rtsp_reply_error(c, RTSP_STATUS_STATE); 03237 return; 03238 } 03239 03240 rtp_c->state = HTTPSTATE_SEND_DATA; 03241 03242 /* now everything is OK, so we can send the connection parameters */ 03243 rtsp_reply_header(c, RTSP_STATUS_OK); 03244 /* session ID */ 03245 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id); 03246 url_fprintf(c->pb, "\r\n"); 03247 } 03248 03249 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPMessageHeader *h) 03250 { 03251 HTTPContext *rtp_c; 03252 03253 rtp_c = find_rtp_session_with_url(url, h->session_id); 03254 if (!rtp_c) { 03255 rtsp_reply_error(c, RTSP_STATUS_SESSION); 03256 return; 03257 } 03258 03259 if (rtp_c->state != HTTPSTATE_SEND_DATA && 03260 rtp_c->state != HTTPSTATE_WAIT_FEED) { 03261 rtsp_reply_error(c, RTSP_STATUS_STATE); 03262 return; 03263 } 03264 03265 rtp_c->state = HTTPSTATE_READY; 03266 rtp_c->first_pts = AV_NOPTS_VALUE; 03267 /* now everything is OK, so we can send the connection parameters */ 03268 rtsp_reply_header(c, RTSP_STATUS_OK); 03269 /* session ID */ 03270 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id); 03271 url_fprintf(c->pb, "\r\n"); 03272 } 03273 03274 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPMessageHeader *h) 03275 { 03276 HTTPContext *rtp_c; 03277 char session_id[32]; 03278 03279 rtp_c = find_rtp_session_with_url(url, h->session_id); 03280 if (!rtp_c) { 03281 rtsp_reply_error(c, RTSP_STATUS_SESSION); 03282 return; 03283 } 03284 03285 av_strlcpy(session_id, rtp_c->session_id, sizeof(session_id)); 03286 03287 /* abort the session */ 03288 close_connection(rtp_c); 03289 03290 /* now everything is OK, so we can send the connection parameters */ 03291 rtsp_reply_header(c, RTSP_STATUS_OK); 03292 /* session ID */ 03293 url_fprintf(c->pb, "Session: %s\r\n", session_id); 03294 url_fprintf(c->pb, "\r\n"); 03295 } 03296 03297 03298 /********************************************************************/ 03299 /* RTP handling */ 03300 03301 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr, 03302 FFStream *stream, const char *session_id, 03303 enum RTSPLowerTransport rtp_protocol) 03304 { 03305 HTTPContext *c = NULL; 03306 const char *proto_str; 03307 03308 /* XXX: should output a warning page when coming 03309 close to the connection limit */ 03310 if (nb_connections >= nb_max_connections) 03311 goto fail; 03312 03313 /* add a new connection */ 03314 c = av_mallocz(sizeof(HTTPContext)); 03315 if (!c) 03316 goto fail; 03317 03318 c->fd = -1; 03319 c->poll_entry = NULL; 03320 c->from_addr = *from_addr; 03321 c->buffer_size = IOBUFFER_INIT_SIZE; 03322 c->buffer = av_malloc(c->buffer_size); 03323 if (!c->buffer) 03324 goto fail; 03325 nb_connections++; 03326 c->stream = stream; 03327 av_strlcpy(c->session_id, session_id, sizeof(c->session_id)); 03328 c->state = HTTPSTATE_READY; 03329 c->is_packetized = 1; 03330 c->rtp_protocol = rtp_protocol; 03331 03332 /* protocol is shown in statistics */ 03333 switch(c->rtp_protocol) { 03334 case RTSP_LOWER_TRANSPORT_UDP_MULTICAST: 03335 proto_str = "MCAST"; 03336 break; 03337 case RTSP_LOWER_TRANSPORT_UDP: 03338 proto_str = "UDP"; 03339 break; 03340 case RTSP_LOWER_TRANSPORT_TCP: 03341 proto_str = "TCP"; 03342 break; 03343 default: 03344 proto_str = "???"; 03345 break; 03346 } 03347 av_strlcpy(c->protocol, "RTP/", sizeof(c->protocol)); 03348 av_strlcat(c->protocol, proto_str, sizeof(c->protocol)); 03349 03350 current_bandwidth += stream->bandwidth; 03351 03352 c->next = first_http_ctx; 03353 first_http_ctx = c; 03354 return c; 03355 03356 fail: 03357 if (c) { 03358 av_free(c->buffer); 03359 av_free(c); 03360 } 03361 return NULL; 03362 } 03363 03364 /* add a new RTP stream in an RTP connection (used in RTSP SETUP 03365 command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is 03366 used. */ 03367 static int rtp_new_av_stream(HTTPContext *c, 03368 int stream_index, struct sockaddr_in *dest_addr, 03369 HTTPContext *rtsp_c) 03370 { 03371 AVFormatContext *ctx; 03372 AVStream *st; 03373 char *ipaddr; 03374 URLContext *h = NULL; 03375 uint8_t *dummy_buf; 03376 int max_packet_size; 03377 03378 /* now we can open the relevant output stream */ 03379 ctx = avformat_alloc_context(); 03380 if (!ctx) 03381 return -1; 03382 ctx->oformat = av_guess_format("rtp", NULL, NULL); 03383 03384 st = av_mallocz(sizeof(AVStream)); 03385 if (!st) 03386 goto fail; 03387 ctx->nb_streams = 1; 03388 ctx->streams[0] = st; 03389 03390 if (!c->stream->feed || 03391 c->stream->feed == c->stream) 03392 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream)); 03393 else 03394 memcpy(st, 03395 c->stream->feed->streams[c->stream->feed_streams[stream_index]], 03396 sizeof(AVStream)); 03397 st->priv_data = NULL; 03398 03399 /* build destination RTP address */ 03400 ipaddr = inet_ntoa(dest_addr->sin_addr); 03401 03402 switch(c->rtp_protocol) { 03403 case RTSP_LOWER_TRANSPORT_UDP: 03404 case RTSP_LOWER_TRANSPORT_UDP_MULTICAST: 03405 /* RTP/UDP case */ 03406 03407 /* XXX: also pass as parameter to function ? */ 03408 if (c->stream->is_multicast) { 03409 int ttl; 03410 ttl = c->stream->multicast_ttl; 03411 if (!ttl) 03412 ttl = 16; 03413 snprintf(ctx->filename, sizeof(ctx->filename), 03414 "rtp://%s:%d?multicast=1&ttl=%d", 03415 ipaddr, ntohs(dest_addr->sin_port), ttl); 03416 } else { 03417 snprintf(ctx->filename, sizeof(ctx->filename), 03418 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port)); 03419 } 03420 03421 if (url_open(&h, ctx->filename, URL_WRONLY) < 0) 03422 goto fail; 03423 c->rtp_handles[stream_index] = h; 03424 max_packet_size = url_get_max_packet_size(h); 03425 break; 03426 case RTSP_LOWER_TRANSPORT_TCP: 03427 /* RTP/TCP case */ 03428 c->rtsp_c = rtsp_c; 03429 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE; 03430 break; 03431 default: 03432 goto fail; 03433 } 03434 03435 http_log("%s:%d - - \"PLAY %s/streamid=%d %s\"\n", 03436 ipaddr, ntohs(dest_addr->sin_port), 03437 c->stream->filename, stream_index, c->protocol); 03438 03439 /* normally, no packets should be output here, but the packet size may be checked */ 03440 if (url_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) { 03441 /* XXX: close stream */ 03442 goto fail; 03443 } 03444 av_set_parameters(ctx, NULL); 03445 if (av_write_header(ctx) < 0) { 03446 fail: 03447 if (h) 03448 url_close(h); 03449 av_free(ctx); 03450 return -1; 03451 } 03452 url_close_dyn_buf(ctx->pb, &dummy_buf); 03453 av_free(dummy_buf); 03454 03455 c->rtp_ctx[stream_index] = ctx; 03456 return 0; 03457 } 03458 03459 /********************************************************************/ 03460 /* ffserver initialization */ 03461 03462 static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec, int copy) 03463 { 03464 AVStream *fst; 03465 03466 fst = av_mallocz(sizeof(AVStream)); 03467 if (!fst) 03468 return NULL; 03469 if (copy) { 03470 fst->codec= avcodec_alloc_context(); 03471 memcpy(fst->codec, codec, sizeof(AVCodecContext)); 03472 if (codec->extradata_size) { 03473 fst->codec->extradata = av_malloc(codec->extradata_size); 03474 memcpy(fst->codec->extradata, codec->extradata, 03475 codec->extradata_size); 03476 } 03477 } else { 03478 /* live streams must use the actual feed's codec since it may be 03479 * updated later to carry extradata needed by the streams. 03480 */ 03481 fst->codec = codec; 03482 } 03483 fst->priv_data = av_mallocz(sizeof(FeedData)); 03484 fst->index = stream->nb_streams; 03485 av_set_pts_info(fst, 33, 1, 90000); 03486 stream->streams[stream->nb_streams++] = fst; 03487 return fst; 03488 } 03489 03490 /* return the stream number in the feed */ 03491 static int add_av_stream(FFStream *feed, AVStream *st) 03492 { 03493 AVStream *fst; 03494 AVCodecContext *av, *av1; 03495 int i; 03496 03497 av = st->codec; 03498 for(i=0;i<feed->nb_streams;i++) { 03499 st = feed->streams[i]; 03500 av1 = st->codec; 03501 if (av1->codec_id == av->codec_id && 03502 av1->codec_type == av->codec_type && 03503 av1->bit_rate == av->bit_rate) { 03504 03505 switch(av->codec_type) { 03506 case AVMEDIA_TYPE_AUDIO: 03507 if (av1->channels == av->channels && 03508 av1->sample_rate == av->sample_rate) 03509 goto found; 03510 break; 03511 case AVMEDIA_TYPE_VIDEO: 03512 if (av1->width == av->width && 03513 av1->height == av->height && 03514 av1->time_base.den == av->time_base.den && 03515 av1->time_base.num == av->time_base.num && 03516 av1->gop_size == av->gop_size) 03517 goto found; 03518 break; 03519 default: 03520 abort(); 03521 } 03522 } 03523 } 03524 03525 fst = add_av_stream1(feed, av, 0); 03526 if (!fst) 03527 return -1; 03528 return feed->nb_streams - 1; 03529 found: 03530 return i; 03531 } 03532 03533 static void remove_stream(FFStream *stream) 03534 { 03535 FFStream **ps; 03536 ps = &first_stream; 03537 while (*ps != NULL) { 03538 if (*ps == stream) 03539 *ps = (*ps)->next; 03540 else 03541 ps = &(*ps)->next; 03542 } 03543 } 03544 03545 /* specific mpeg4 handling : we extract the raw parameters */ 03546 static void extract_mpeg4_header(AVFormatContext *infile) 03547 { 03548 int mpeg4_count, i, size; 03549 AVPacket pkt; 03550 AVStream *st; 03551 const uint8_t *p; 03552 03553 mpeg4_count = 0; 03554 for(i=0;i<infile->nb_streams;i++) { 03555 st = infile->streams[i]; 03556 if (st->codec->codec_id == CODEC_ID_MPEG4 && 03557 st->codec->extradata_size == 0) { 03558 mpeg4_count++; 03559 } 03560 } 03561 if (!mpeg4_count) 03562 return; 03563 03564 printf("MPEG4 without extra data: trying to find header in %s\n", infile->filename); 03565 while (mpeg4_count > 0) { 03566 if (av_read_packet(infile, &pkt) < 0) 03567 break; 03568 st = infile->streams[pkt.stream_index]; 03569 if (st->codec->codec_id == CODEC_ID_MPEG4 && 03570 st->codec->extradata_size == 0) { 03571 av_freep(&st->codec->extradata); 03572 /* fill extradata with the header */ 03573 /* XXX: we make hard suppositions here ! */ 03574 p = pkt.data; 03575 while (p < pkt.data + pkt.size - 4) { 03576 /* stop when vop header is found */ 03577 if (p[0] == 0x00 && p[1] == 0x00 && 03578 p[2] == 0x01 && p[3] == 0xb6) { 03579 size = p - pkt.data; 03580 // av_hex_dump_log(infile, AV_LOG_DEBUG, pkt.data, size); 03581 st->codec->extradata = av_malloc(size); 03582 st->codec->extradata_size = size; 03583 memcpy(st->codec->extradata, pkt.data, size); 03584 break; 03585 } 03586 p++; 03587 } 03588 mpeg4_count--; 03589 } 03590 av_free_packet(&pkt); 03591 } 03592 } 03593 03594 /* compute the needed AVStream for each file */ 03595 static void build_file_streams(void) 03596 { 03597 FFStream *stream, *stream_next; 03598 AVFormatContext *infile; 03599 int i, ret; 03600 03601 /* gather all streams */ 03602 for(stream = first_stream; stream != NULL; stream = stream_next) { 03603 stream_next = stream->next; 03604 if (stream->stream_type == STREAM_TYPE_LIVE && 03605 !stream->feed) { 03606 /* the stream comes from a file */ 03607 /* try to open the file */ 03608 /* open stream */ 03609 stream->ap_in = av_mallocz(sizeof(AVFormatParameters)); 03610 if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) { 03611 /* specific case : if transport stream output to RTP, 03612 we use a raw transport stream reader */ 03613 stream->ap_in->mpeg2ts_raw = 1; 03614 stream->ap_in->mpeg2ts_compute_pcr = 1; 03615 } 03616 03617 http_log("Opening file '%s'\n", stream->feed_filename); 03618 if ((ret = av_open_input_file(&infile, stream->feed_filename, 03619 stream->ifmt, 0, stream->ap_in)) < 0) { 03620 http_log("Could not open '%s': %d\n", stream->feed_filename, ret); 03621 /* remove stream (no need to spend more time on it) */ 03622 fail: 03623 remove_stream(stream); 03624 } else { 03625 /* find all the AVStreams inside and reference them in 03626 'stream' */ 03627 if (av_find_stream_info(infile) < 0) { 03628 http_log("Could not find codec parameters from '%s'\n", 03629 stream->feed_filename); 03630 av_close_input_file(infile); 03631 goto fail; 03632 } 03633 extract_mpeg4_header(infile); 03634 03635 for(i=0;i<infile->nb_streams;i++) 03636 add_av_stream1(stream, infile->streams[i]->codec, 1); 03637 03638 av_close_input_file(infile); 03639 } 03640 } 03641 } 03642 } 03643 03644 /* compute the needed AVStream for each feed */ 03645 static void build_feed_streams(void) 03646 { 03647 FFStream *stream, *feed; 03648 int i; 03649 03650 /* gather all streams */ 03651 for(stream = first_stream; stream != NULL; stream = stream->next) { 03652 feed = stream->feed; 03653 if (feed) { 03654 if (!stream->is_feed) { 03655 /* we handle a stream coming from a feed */ 03656 for(i=0;i<stream->nb_streams;i++) 03657 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]); 03658 } 03659 } 03660 } 03661 03662 /* gather all streams */ 03663 for(stream = first_stream; stream != NULL; stream = stream->next) { 03664 feed = stream->feed; 03665 if (feed) { 03666 if (stream->is_feed) { 03667 for(i=0;i<stream->nb_streams;i++) 03668 stream->feed_streams[i] = i; 03669 } 03670 } 03671 } 03672 03673 /* create feed files if needed */ 03674 for(feed = first_feed; feed != NULL; feed = feed->next_feed) { 03675 int fd; 03676 03677 if (url_exist(feed->feed_filename)) { 03678 /* See if it matches */ 03679 AVFormatContext *s; 03680 int matches = 0; 03681 03682 if (av_open_input_file(&s, feed->feed_filename, NULL, FFM_PACKET_SIZE, NULL) >= 0) { 03683 /* Now see if it matches */ 03684 if (s->nb_streams == feed->nb_streams) { 03685 matches = 1; 03686 for(i=0;i<s->nb_streams;i++) { 03687 AVStream *sf, *ss; 03688 sf = feed->streams[i]; 03689 ss = s->streams[i]; 03690 03691 if (sf->index != ss->index || 03692 sf->id != ss->id) { 03693 http_log("Index & Id do not match for stream %d (%s)\n", 03694 i, feed->feed_filename); 03695 matches = 0; 03696 } else { 03697 AVCodecContext *ccf, *ccs; 03698 03699 ccf = sf->codec; 03700 ccs = ss->codec; 03701 #define CHECK_CODEC(x) (ccf->x != ccs->x) 03702 03703 if (CHECK_CODEC(codec_id) || CHECK_CODEC(codec_type)) { 03704 http_log("Codecs do not match for stream %d\n", i); 03705 matches = 0; 03706 } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) { 03707 http_log("Codec bitrates do not match for stream %d\n", i); 03708 matches = 0; 03709 } else if (ccf->codec_type == AVMEDIA_TYPE_VIDEO) { 03710 if (CHECK_CODEC(time_base.den) || 03711 CHECK_CODEC(time_base.num) || 03712 CHECK_CODEC(width) || 03713 CHECK_CODEC(height)) { 03714 http_log("Codec width, height and framerate do not match for stream %d\n", i); 03715 matches = 0; 03716 } 03717 } else if (ccf->codec_type == AVMEDIA_TYPE_AUDIO) { 03718 if (CHECK_CODEC(sample_rate) || 03719 CHECK_CODEC(channels) || 03720 CHECK_CODEC(frame_size)) { 03721 http_log("Codec sample_rate, channels, frame_size do not match for stream %d\n", i); 03722 matches = 0; 03723 } 03724 } else { 03725 http_log("Unknown codec type\n"); 03726 matches = 0; 03727 } 03728 } 03729 if (!matches) 03730 break; 03731 } 03732 } else 03733 http_log("Deleting feed file '%s' as stream counts differ (%d != %d)\n", 03734 feed->feed_filename, s->nb_streams, feed->nb_streams); 03735 03736 av_close_input_file(s); 03737 } else 03738 http_log("Deleting feed file '%s' as it appears to be corrupt\n", 03739 feed->feed_filename); 03740 03741 if (!matches) { 03742 if (feed->readonly) { 03743 http_log("Unable to delete feed file '%s' as it is marked readonly\n", 03744 feed->feed_filename); 03745 exit(1); 03746 } 03747 unlink(feed->feed_filename); 03748 } 03749 } 03750 if (!url_exist(feed->feed_filename)) { 03751 AVFormatContext s1 = {0}, *s = &s1; 03752 03753 if (feed->readonly) { 03754 http_log("Unable to create feed file '%s' as it is marked readonly\n", 03755 feed->feed_filename); 03756 exit(1); 03757 } 03758 03759 /* only write the header of the ffm file */ 03760 if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) { 03761 http_log("Could not open output feed file '%s'\n", 03762 feed->feed_filename); 03763 exit(1); 03764 } 03765 s->oformat = feed->fmt; 03766 s->nb_streams = feed->nb_streams; 03767 for(i=0;i<s->nb_streams;i++) { 03768 AVStream *st; 03769 st = feed->streams[i]; 03770 s->streams[i] = st; 03771 } 03772 av_set_parameters(s, NULL); 03773 if (av_write_header(s) < 0) { 03774 http_log("Container doesn't supports the required parameters\n"); 03775 exit(1); 03776 } 03777 /* XXX: need better api */ 03778 av_freep(&s->priv_data); 03779 url_fclose(s->pb); 03780 } 03781 /* get feed size and write index */ 03782 fd = open(feed->feed_filename, O_RDONLY); 03783 if (fd < 0) { 03784 http_log("Could not open output feed file '%s'\n", 03785 feed->feed_filename); 03786 exit(1); 03787 } 03788 03789 feed->feed_write_index = FFMAX(ffm_read_write_index(fd), FFM_PACKET_SIZE); 03790 feed->feed_size = lseek(fd, 0, SEEK_END); 03791 /* ensure that we do not wrap before the end of file */ 03792 if (feed->feed_max_size && feed->feed_max_size < feed->feed_size) 03793 feed->feed_max_size = feed->feed_size; 03794 03795 close(fd); 03796 } 03797 } 03798 03799 /* compute the bandwidth used by each stream */ 03800 static void compute_bandwidth(void) 03801 { 03802 unsigned bandwidth; 03803 int i; 03804 FFStream *stream; 03805 03806 for(stream = first_stream; stream != NULL; stream = stream->next) { 03807 bandwidth = 0; 03808 for(i=0;i<stream->nb_streams;i++) { 03809 AVStream *st = stream->streams[i]; 03810 switch(st->codec->codec_type) { 03811 case AVMEDIA_TYPE_AUDIO: 03812 case AVMEDIA_TYPE_VIDEO: 03813 bandwidth += st->codec->bit_rate; 03814 break; 03815 default: 03816 break; 03817 } 03818 } 03819 stream->bandwidth = (bandwidth + 999) / 1000; 03820 } 03821 } 03822 03823 /* add a codec and set the default parameters */ 03824 static void add_codec(FFStream *stream, AVCodecContext *av) 03825 { 03826 AVStream *st; 03827 03828 /* compute default parameters */ 03829 switch(av->codec_type) { 03830 case AVMEDIA_TYPE_AUDIO: 03831 if (av->bit_rate == 0) 03832 av->bit_rate = 64000; 03833 if (av->sample_rate == 0) 03834 av->sample_rate = 22050; 03835 if (av->channels == 0) 03836 av->channels = 1; 03837 break; 03838 case AVMEDIA_TYPE_VIDEO: 03839 if (av->bit_rate == 0) 03840 av->bit_rate = 64000; 03841 if (av->time_base.num == 0){ 03842 av->time_base.den = 5; 03843 av->time_base.num = 1; 03844 } 03845 if (av->width == 0 || av->height == 0) { 03846 av->width = 160; 03847 av->height = 128; 03848 } 03849 /* Bitrate tolerance is less for streaming */ 03850 if (av->bit_rate_tolerance == 0) 03851 av->bit_rate_tolerance = FFMAX(av->bit_rate / 4, 03852 (int64_t)av->bit_rate*av->time_base.num/av->time_base.den); 03853 if (av->qmin == 0) 03854 av->qmin = 3; 03855 if (av->qmax == 0) 03856 av->qmax = 31; 03857 if (av->max_qdiff == 0) 03858 av->max_qdiff = 3; 03859 av->qcompress = 0.5; 03860 av->qblur = 0.5; 03861 03862 if (!av->nsse_weight) 03863 av->nsse_weight = 8; 03864 03865 av->frame_skip_cmp = FF_CMP_DCTMAX; 03866 av->me_method = ME_EPZS; 03867 av->rc_buffer_aggressivity = 1.0; 03868 03869 if (!av->rc_eq) 03870 av->rc_eq = "tex^qComp"; 03871 if (!av->i_quant_factor) 03872 av->i_quant_factor = -0.8; 03873 if (!av->b_quant_factor) 03874 av->b_quant_factor = 1.25; 03875 if (!av->b_quant_offset) 03876 av->b_quant_offset = 1.25; 03877 if (!av->rc_max_rate) 03878 av->rc_max_rate = av->bit_rate * 2; 03879 03880 if (av->rc_max_rate && !av->rc_buffer_size) { 03881 av->rc_buffer_size = av->rc_max_rate; 03882 } 03883 03884 03885 break; 03886 default: 03887 abort(); 03888 } 03889 03890 st = av_mallocz(sizeof(AVStream)); 03891 if (!st) 03892 return; 03893 st->codec = avcodec_alloc_context(); 03894 stream->streams[stream->nb_streams++] = st; 03895 memcpy(st->codec, av, sizeof(AVCodecContext)); 03896 } 03897 03898 static enum CodecID opt_audio_codec(const char *arg) 03899 { 03900 AVCodec *p= avcodec_find_encoder_by_name(arg); 03901 03902 if (p == NULL || p->type != AVMEDIA_TYPE_AUDIO) 03903 return CODEC_ID_NONE; 03904 03905 return p->id; 03906 } 03907 03908 static enum CodecID opt_video_codec(const char *arg) 03909 { 03910 AVCodec *p= avcodec_find_encoder_by_name(arg); 03911 03912 if (p == NULL || p->type != AVMEDIA_TYPE_VIDEO) 03913 return CODEC_ID_NONE; 03914 03915 return p->id; 03916 } 03917 03918 /* simplistic plugin support */ 03919 03920 #if HAVE_DLOPEN 03921 static void load_module(const char *filename) 03922 { 03923 void *dll; 03924 void (*init_func)(void); 03925 dll = dlopen(filename, RTLD_NOW); 03926 if (!dll) { 03927 fprintf(stderr, "Could not load module '%s' - %s\n", 03928 filename, dlerror()); 03929 return; 03930 } 03931 03932 init_func = dlsym(dll, "ffserver_module_init"); 03933 if (!init_func) { 03934 fprintf(stderr, 03935 "%s: init function 'ffserver_module_init()' not found\n", 03936 filename); 03937 dlclose(dll); 03938 } 03939 03940 init_func(); 03941 } 03942 #endif 03943 03944 static int ffserver_opt_default(const char *opt, const char *arg, 03945 AVCodecContext *avctx, int type) 03946 { 03947 int ret = 0; 03948 const AVOption *o = av_find_opt(avctx, opt, NULL, type, type); 03949 if(o) 03950 ret = av_set_string3(avctx, opt, arg, 1, NULL); 03951 return ret; 03952 } 03953 03954 static AVOutputFormat *ffserver_guess_format(const char *short_name, const char *filename, 03955 const char *mime_type) 03956 { 03957 AVOutputFormat *fmt = av_guess_format(short_name, filename, mime_type); 03958 03959 if (fmt) { 03960 AVOutputFormat *stream_fmt; 03961 char stream_format_name[64]; 03962 03963 snprintf(stream_format_name, sizeof(stream_format_name), "%s_stream", fmt->name); 03964 stream_fmt = av_guess_format(stream_format_name, NULL, NULL); 03965 03966 if (stream_fmt) 03967 fmt = stream_fmt; 03968 } 03969 03970 return fmt; 03971 } 03972 03973 static void report_config_error(const char *filename, int line_num, int *errors, const char *fmt, ...) 03974 { 03975 va_list vl; 03976 va_start(vl, fmt); 03977 fprintf(stderr, "%s:%d: ", filename, line_num); 03978 vfprintf(stderr, fmt, vl); 03979 va_end(vl); 03980 03981 (*errors)++; 03982 } 03983 03984 static int parse_ffconfig(const char *filename) 03985 { 03986 FILE *f; 03987 char line[1024]; 03988 char cmd[64]; 03989 char arg[1024]; 03990 const char *p; 03991 int val, errors, line_num; 03992 FFStream **last_stream, *stream, *redirect; 03993 FFStream **last_feed, *feed, *s; 03994 AVCodecContext audio_enc, video_enc; 03995 enum CodecID audio_id, video_id; 03996 03997 f = fopen(filename, "r"); 03998 if (!f) { 03999 perror(filename); 04000 return -1; 04001 } 04002 04003 errors = 0; 04004 line_num = 0; 04005 first_stream = NULL; 04006 last_stream = &first_stream; 04007 first_feed = NULL; 04008 last_feed = &first_feed; 04009 stream = NULL; 04010 feed = NULL; 04011 redirect = NULL; 04012 audio_id = CODEC_ID_NONE; 04013 video_id = CODEC_ID_NONE; 04014 04015 #define ERROR(...) report_config_error(filename, line_num, &errors, __VA_ARGS__) 04016 for(;;) { 04017 if (fgets(line, sizeof(line), f) == NULL) 04018 break; 04019 line_num++; 04020 p = line; 04021 while (isspace(*p)) 04022 p++; 04023 if (*p == '\0' || *p == '#') 04024 continue; 04025 04026 get_arg(cmd, sizeof(cmd), &p); 04027 04028 if (!strcasecmp(cmd, "Port")) { 04029 get_arg(arg, sizeof(arg), &p); 04030 val = atoi(arg); 04031 if (val < 1 || val > 65536) { 04032 ERROR("Invalid_port: %s\n", arg); 04033 } 04034 my_http_addr.sin_port = htons(val); 04035 } else if (!strcasecmp(cmd, "BindAddress")) { 04036 get_arg(arg, sizeof(arg), &p); 04037 if (resolve_host(&my_http_addr.sin_addr, arg) != 0) { 04038 ERROR("%s:%d: Invalid host/IP address: %s\n", arg); 04039 } 04040 } else if (!strcasecmp(cmd, "NoDaemon")) { 04041 ffserver_daemon = 0; 04042 } else if (!strcasecmp(cmd, "RTSPPort")) { 04043 get_arg(arg, sizeof(arg), &p); 04044 val = atoi(arg); 04045 if (val < 1 || val > 65536) { 04046 ERROR("%s:%d: Invalid port: %s\n", arg); 04047 } 04048 my_rtsp_addr.sin_port = htons(atoi(arg)); 04049 } else if (!strcasecmp(cmd, "RTSPBindAddress")) { 04050 get_arg(arg, sizeof(arg), &p); 04051 if (resolve_host(&my_rtsp_addr.sin_addr, arg) != 0) { 04052 ERROR("Invalid host/IP address: %s\n", arg); 04053 } 04054 } else if (!strcasecmp(cmd, "MaxHTTPConnections")) { 04055 get_arg(arg, sizeof(arg), &p); 04056 val = atoi(arg); 04057 if (val < 1 || val > 65536) { 04058 ERROR("Invalid MaxHTTPConnections: %s\n", arg); 04059 } 04060 nb_max_http_connections = val; 04061 } else if (!strcasecmp(cmd, "MaxClients")) { 04062 get_arg(arg, sizeof(arg), &p); 04063 val = atoi(arg); 04064 if (val < 1 || val > nb_max_http_connections) { 04065 ERROR("Invalid MaxClients: %s\n", arg); 04066 } else { 04067 nb_max_connections = val; 04068 } 04069 } else if (!strcasecmp(cmd, "MaxBandwidth")) { 04070 int64_t llval; 04071 get_arg(arg, sizeof(arg), &p); 04072 llval = atoll(arg); 04073 if (llval < 10 || llval > 10000000) { 04074 ERROR("Invalid MaxBandwidth: %s\n", arg); 04075 } else 04076 max_bandwidth = llval; 04077 } else if (!strcasecmp(cmd, "CustomLog")) { 04078 if (!ffserver_debug) 04079 get_arg(logfilename, sizeof(logfilename), &p); 04080 } else if (!strcasecmp(cmd, "<Feed")) { 04081 /*********************************************/ 04082 /* Feed related options */ 04083 char *q; 04084 if (stream || feed) { 04085 ERROR("Already in a tag\n"); 04086 } else { 04087 feed = av_mallocz(sizeof(FFStream)); 04088 get_arg(feed->filename, sizeof(feed->filename), &p); 04089 q = strrchr(feed->filename, '>'); 04090 if (*q) 04091 *q = '\0'; 04092 04093 for (s = first_feed; s; s = s->next) { 04094 if (!strcmp(feed->filename, s->filename)) { 04095 ERROR("Feed '%s' already registered\n", s->filename); 04096 } 04097 } 04098 04099 feed->fmt = av_guess_format("ffm", NULL, NULL); 04100 /* defaut feed file */ 04101 snprintf(feed->feed_filename, sizeof(feed->feed_filename), 04102 "/tmp/%s.ffm", feed->filename); 04103 feed->feed_max_size = 5 * 1024 * 1024; 04104 feed->is_feed = 1; 04105 feed->feed = feed; /* self feeding :-) */ 04106 04107 /* add in stream list */ 04108 *last_stream = feed; 04109 last_stream = &feed->next; 04110 /* add in feed list */ 04111 *last_feed = feed; 04112 last_feed = &feed->next_feed; 04113 } 04114 } else if (!strcasecmp(cmd, "Launch")) { 04115 if (feed) { 04116 int i; 04117 04118 feed->child_argv = av_mallocz(64 * sizeof(char *)); 04119 04120 for (i = 0; i < 62; i++) { 04121 get_arg(arg, sizeof(arg), &p); 04122 if (!arg[0]) 04123 break; 04124 04125 feed->child_argv[i] = av_strdup(arg); 04126 } 04127 04128 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename)); 04129 04130 snprintf(feed->child_argv[i], 30+strlen(feed->filename), 04131 "http://%s:%d/%s", 04132 (my_http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" : 04133 inet_ntoa(my_http_addr.sin_addr), 04134 ntohs(my_http_addr.sin_port), feed->filename); 04135 } 04136 } else if (!strcasecmp(cmd, "ReadOnlyFile")) { 04137 if (feed) { 04138 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p); 04139 feed->readonly = 1; 04140 } else if (stream) { 04141 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p); 04142 } 04143 } else if (!strcasecmp(cmd, "File")) { 04144 if (feed) { 04145 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p); 04146 } else if (stream) 04147 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p); 04148 } else if (!strcasecmp(cmd, "Truncate")) { 04149 if (feed) { 04150 get_arg(arg, sizeof(arg), &p); 04151 feed->truncate = strtod(arg, NULL); 04152 } 04153 } else if (!strcasecmp(cmd, "FileMaxSize")) { 04154 if (feed) { 04155 char *p1; 04156 double fsize; 04157 04158 get_arg(arg, sizeof(arg), &p); 04159 p1 = arg; 04160 fsize = strtod(p1, &p1); 04161 switch(toupper(*p1)) { 04162 case 'K': 04163 fsize *= 1024; 04164 break; 04165 case 'M': 04166 fsize *= 1024 * 1024; 04167 break; 04168 case 'G': 04169 fsize *= 1024 * 1024 * 1024; 04170 break; 04171 } 04172 feed->feed_max_size = (int64_t)fsize; 04173 if (feed->feed_max_size < FFM_PACKET_SIZE*4) { 04174 ERROR("Feed max file size is too small, must be at least %d\n", FFM_PACKET_SIZE*4); 04175 } 04176 } 04177 } else if (!strcasecmp(cmd, "</Feed>")) { 04178 if (!feed) { 04179 ERROR("No corresponding <Feed> for </Feed>\n"); 04180 } 04181 feed = NULL; 04182 } else if (!strcasecmp(cmd, "<Stream")) { 04183 /*********************************************/ 04184 /* Stream related options */ 04185 char *q; 04186 if (stream || feed) { 04187 ERROR("Already in a tag\n"); 04188 } else { 04189 FFStream *s; 04190 stream = av_mallocz(sizeof(FFStream)); 04191 get_arg(stream->filename, sizeof(stream->filename), &p); 04192 q = strrchr(stream->filename, '>'); 04193 if (*q) 04194 *q = '\0'; 04195 04196 for (s = first_stream; s; s = s->next) { 04197 if (!strcmp(stream->filename, s->filename)) { 04198 ERROR("Stream '%s' already registered\n", s->filename); 04199 } 04200 } 04201 04202 stream->fmt = ffserver_guess_format(NULL, stream->filename, NULL); 04203 avcodec_get_context_defaults2(&video_enc, AVMEDIA_TYPE_VIDEO); 04204 avcodec_get_context_defaults2(&audio_enc, AVMEDIA_TYPE_AUDIO); 04205 audio_id = CODEC_ID_NONE; 04206 video_id = CODEC_ID_NONE; 04207 if (stream->fmt) { 04208 audio_id = stream->fmt->audio_codec; 04209 video_id = stream->fmt->video_codec; 04210 } 04211 04212 *last_stream = stream; 04213 last_stream = &stream->next; 04214 } 04215 } else if (!strcasecmp(cmd, "Feed")) { 04216 get_arg(arg, sizeof(arg), &p); 04217 if (stream) { 04218 FFStream *sfeed; 04219 04220 sfeed = first_feed; 04221 while (sfeed != NULL) { 04222 if (!strcmp(sfeed->filename, arg)) 04223 break; 04224 sfeed = sfeed->next_feed; 04225 } 04226 if (!sfeed) 04227 ERROR("feed '%s' not defined\n", arg); 04228 else 04229 stream->feed = sfeed; 04230 } 04231 } else if (!strcasecmp(cmd, "Format")) { 04232 get_arg(arg, sizeof(arg), &p); 04233 if (stream) { 04234 if (!strcmp(arg, "status")) { 04235 stream->stream_type = STREAM_TYPE_STATUS; 04236 stream->fmt = NULL; 04237 } else { 04238 stream->stream_type = STREAM_TYPE_LIVE; 04239 /* jpeg cannot be used here, so use single frame jpeg */ 04240 if (!strcmp(arg, "jpeg")) 04241 strcpy(arg, "mjpeg"); 04242 stream->fmt = ffserver_guess_format(arg, NULL, NULL); 04243 if (!stream->fmt) { 04244 ERROR("Unknown Format: %s\n", arg); 04245 } 04246 } 04247 if (stream->fmt) { 04248 audio_id = stream->fmt->audio_codec; 04249 video_id = stream->fmt->video_codec; 04250 } 04251 } 04252 } else if (!strcasecmp(cmd, "InputFormat")) { 04253 get_arg(arg, sizeof(arg), &p); 04254 if (stream) { 04255 stream->ifmt = av_find_input_format(arg); 04256 if (!stream->ifmt) { 04257 ERROR("Unknown input format: %s\n", arg); 04258 } 04259 } 04260 } else if (!strcasecmp(cmd, "FaviconURL")) { 04261 if (stream && stream->stream_type == STREAM_TYPE_STATUS) { 04262 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p); 04263 } else { 04264 ERROR("FaviconURL only permitted for status streams\n"); 04265 } 04266 } else if (!strcasecmp(cmd, "Author")) { 04267 if (stream) 04268 get_arg(stream->author, sizeof(stream->author), &p); 04269 } else if (!strcasecmp(cmd, "Comment")) { 04270 if (stream) 04271 get_arg(stream->comment, sizeof(stream->comment), &p); 04272 } else if (!strcasecmp(cmd, "Copyright")) { 04273 if (stream) 04274 get_arg(stream->copyright, sizeof(stream->copyright), &p); 04275 } else if (!strcasecmp(cmd, "Title")) { 04276 if (stream) 04277 get_arg(stream->title, sizeof(stream->title), &p); 04278 } else if (!strcasecmp(cmd, "Preroll")) { 04279 get_arg(arg, sizeof(arg), &p); 04280 if (stream) 04281 stream->prebuffer = atof(arg) * 1000; 04282 } else if (!strcasecmp(cmd, "StartSendOnKey")) { 04283 if (stream) 04284 stream->send_on_key = 1; 04285 } else if (!strcasecmp(cmd, "AudioCodec")) { 04286 get_arg(arg, sizeof(arg), &p); 04287 audio_id = opt_audio_codec(arg); 04288 if (audio_id == CODEC_ID_NONE) { 04289 ERROR("Unknown AudioCodec: %s\n", arg); 04290 } 04291 } else if (!strcasecmp(cmd, "VideoCodec")) { 04292 get_arg(arg, sizeof(arg), &p); 04293 video_id = opt_video_codec(arg); 04294 if (video_id == CODEC_ID_NONE) { 04295 ERROR("Unknown VideoCodec: %s\n", arg); 04296 } 04297 } else if (!strcasecmp(cmd, "MaxTime")) { 04298 get_arg(arg, sizeof(arg), &p); 04299 if (stream) 04300 stream->max_time = atof(arg) * 1000; 04301 } else if (!strcasecmp(cmd, "AudioBitRate")) { 04302 get_arg(arg, sizeof(arg), &p); 04303 if (stream) 04304 audio_enc.bit_rate = atoi(arg) * 1000; 04305 } else if (!strcasecmp(cmd, "AudioChannels")) { 04306 get_arg(arg, sizeof(arg), &p); 04307 if (stream) 04308 audio_enc.channels = atoi(arg); 04309 } else if (!strcasecmp(cmd, "AudioSampleRate")) { 04310 get_arg(arg, sizeof(arg), &p); 04311 if (stream) 04312 audio_enc.sample_rate = atoi(arg); 04313 } else if (!strcasecmp(cmd, "AudioQuality")) { 04314 get_arg(arg, sizeof(arg), &p); 04315 if (stream) { 04316 // audio_enc.quality = atof(arg) * 1000; 04317 } 04318 } else if (!strcasecmp(cmd, "VideoBitRateRange")) { 04319 if (stream) { 04320 int minrate, maxrate; 04321 04322 get_arg(arg, sizeof(arg), &p); 04323 04324 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) { 04325 video_enc.rc_min_rate = minrate * 1000; 04326 video_enc.rc_max_rate = maxrate * 1000; 04327 } else { 04328 ERROR("Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n", arg); 04329 } 04330 } 04331 } else if (!strcasecmp(cmd, "Debug")) { 04332 if (stream) { 04333 get_arg(arg, sizeof(arg), &p); 04334 video_enc.debug = strtol(arg,0,0); 04335 } 04336 } else if (!strcasecmp(cmd, "Strict")) { 04337 if (stream) { 04338 get_arg(arg, sizeof(arg), &p); 04339 video_enc.strict_std_compliance = atoi(arg); 04340 } 04341 } else if (!strcasecmp(cmd, "VideoBufferSize")) { 04342 if (stream) { 04343 get_arg(arg, sizeof(arg), &p); 04344 video_enc.rc_buffer_size = atoi(arg) * 8*1024; 04345 } 04346 } else if (!strcasecmp(cmd, "VideoBitRateTolerance")) { 04347 if (stream) { 04348 get_arg(arg, sizeof(arg), &p); 04349 video_enc.bit_rate_tolerance = atoi(arg) * 1000; 04350 } 04351 } else if (!strcasecmp(cmd, "VideoBitRate")) { 04352 get_arg(arg, sizeof(arg), &p); 04353 if (stream) { 04354 video_enc.bit_rate = atoi(arg) * 1000; 04355 } 04356 } else if (!strcasecmp(cmd, "VideoSize")) { 04357 get_arg(arg, sizeof(arg), &p); 04358 if (stream) { 04359 av_parse_video_frame_size(&video_enc.width, &video_enc.height, arg); 04360 if ((video_enc.width % 16) != 0 || 04361 (video_enc.height % 16) != 0) { 04362 ERROR("Image size must be a multiple of 16\n"); 04363 } 04364 } 04365 } else if (!strcasecmp(cmd, "VideoFrameRate")) { 04366 get_arg(arg, sizeof(arg), &p); 04367 if (stream) { 04368 AVRational frame_rate; 04369 if (av_parse_video_frame_rate(&frame_rate, arg) < 0) { 04370 ERROR("Incorrect frame rate: %s\n", arg); 04371 } else { 04372 video_enc.time_base.num = frame_rate.den; 04373 video_enc.time_base.den = frame_rate.num; 04374 } 04375 } 04376 } else if (!strcasecmp(cmd, "VideoGopSize")) { 04377 get_arg(arg, sizeof(arg), &p); 04378 if (stream) 04379 video_enc.gop_size = atoi(arg); 04380 } else if (!strcasecmp(cmd, "VideoIntraOnly")) { 04381 if (stream) 04382 video_enc.gop_size = 1; 04383 } else if (!strcasecmp(cmd, "VideoHighQuality")) { 04384 if (stream) 04385 video_enc.mb_decision = FF_MB_DECISION_BITS; 04386 } else if (!strcasecmp(cmd, "Video4MotionVector")) { 04387 if (stream) { 04388 video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove 04389 video_enc.flags |= CODEC_FLAG_4MV; 04390 } 04391 } else if (!strcasecmp(cmd, "AVOptionVideo") || 04392 !strcasecmp(cmd, "AVOptionAudio")) { 04393 char arg2[1024]; 04394 AVCodecContext *avctx; 04395 int type; 04396 get_arg(arg, sizeof(arg), &p); 04397 get_arg(arg2, sizeof(arg2), &p); 04398 if (!strcasecmp(cmd, "AVOptionVideo")) { 04399 avctx = &video_enc; 04400 type = AV_OPT_FLAG_VIDEO_PARAM; 04401 } else { 04402 avctx = &audio_enc; 04403 type = AV_OPT_FLAG_AUDIO_PARAM; 04404 } 04405 if (ffserver_opt_default(arg, arg2, avctx, type|AV_OPT_FLAG_ENCODING_PARAM)) { 04406 ERROR("AVOption error: %s %s\n", arg, arg2); 04407 } 04408 } else if (!strcasecmp(cmd, "VideoTag")) { 04409 get_arg(arg, sizeof(arg), &p); 04410 if ((strlen(arg) == 4) && stream) 04411 video_enc.codec_tag = MKTAG(arg[0], arg[1], arg[2], arg[3]); 04412 } else if (!strcasecmp(cmd, "BitExact")) { 04413 if (stream) 04414 video_enc.flags |= CODEC_FLAG_BITEXACT; 04415 } else if (!strcasecmp(cmd, "DctFastint")) { 04416 if (stream) 04417 video_enc.dct_algo = FF_DCT_FASTINT; 04418 } else if (!strcasecmp(cmd, "IdctSimple")) { 04419 if (stream) 04420 video_enc.idct_algo = FF_IDCT_SIMPLE; 04421 } else if (!strcasecmp(cmd, "Qscale")) { 04422 get_arg(arg, sizeof(arg), &p); 04423 if (stream) { 04424 video_enc.flags |= CODEC_FLAG_QSCALE; 04425 video_enc.global_quality = FF_QP2LAMBDA * atoi(arg); 04426 } 04427 } else if (!strcasecmp(cmd, "VideoQDiff")) { 04428 get_arg(arg, sizeof(arg), &p); 04429 if (stream) { 04430 video_enc.max_qdiff = atoi(arg); 04431 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) { 04432 ERROR("VideoQDiff out of range\n"); 04433 } 04434 } 04435 } else if (!strcasecmp(cmd, "VideoQMax")) { 04436 get_arg(arg, sizeof(arg), &p); 04437 if (stream) { 04438 video_enc.qmax = atoi(arg); 04439 if (video_enc.qmax < 1 || video_enc.qmax > 31) { 04440 ERROR("VideoQMax out of range\n"); 04441 } 04442 } 04443 } else if (!strcasecmp(cmd, "VideoQMin")) { 04444 get_arg(arg, sizeof(arg), &p); 04445 if (stream) { 04446 video_enc.qmin = atoi(arg); 04447 if (video_enc.qmin < 1 || video_enc.qmin > 31) { 04448 ERROR("VideoQMin out of range\n"); 04449 } 04450 } 04451 } else if (!strcasecmp(cmd, "LumaElim")) { 04452 get_arg(arg, sizeof(arg), &p); 04453 if (stream) 04454 video_enc.luma_elim_threshold = atoi(arg); 04455 } else if (!strcasecmp(cmd, "ChromaElim")) { 04456 get_arg(arg, sizeof(arg), &p); 04457 if (stream) 04458 video_enc.chroma_elim_threshold = atoi(arg); 04459 } else if (!strcasecmp(cmd, "LumiMask")) { 04460 get_arg(arg, sizeof(arg), &p); 04461 if (stream) 04462 video_enc.lumi_masking = atof(arg); 04463 } else if (!strcasecmp(cmd, "DarkMask")) { 04464 get_arg(arg, sizeof(arg), &p); 04465 if (stream) 04466 video_enc.dark_masking = atof(arg); 04467 } else if (!strcasecmp(cmd, "NoVideo")) { 04468 video_id = CODEC_ID_NONE; 04469 } else if (!strcasecmp(cmd, "NoAudio")) { 04470 audio_id = CODEC_ID_NONE; 04471 } else if (!strcasecmp(cmd, "ACL")) { 04472 parse_acl_row(stream, feed, NULL, p, filename, line_num); 04473 } else if (!strcasecmp(cmd, "DynamicACL")) { 04474 if (stream) { 04475 get_arg(stream->dynamic_acl, sizeof(stream->dynamic_acl), &p); 04476 } 04477 } else if (!strcasecmp(cmd, "RTSPOption")) { 04478 get_arg(arg, sizeof(arg), &p); 04479 if (stream) { 04480 av_freep(&stream->rtsp_option); 04481 stream->rtsp_option = av_strdup(arg); 04482 } 04483 } else if (!strcasecmp(cmd, "MulticastAddress")) { 04484 get_arg(arg, sizeof(arg), &p); 04485 if (stream) { 04486 if (resolve_host(&stream->multicast_ip, arg) != 0) { 04487 ERROR("Invalid host/IP address: %s\n", arg); 04488 } 04489 stream->is_multicast = 1; 04490 stream->loop = 1; /* default is looping */ 04491 } 04492 } else if (!strcasecmp(cmd, "MulticastPort")) { 04493 get_arg(arg, sizeof(arg), &p); 04494 if (stream) 04495 stream->multicast_port = atoi(arg); 04496 } else if (!strcasecmp(cmd, "MulticastTTL")) { 04497 get_arg(arg, sizeof(arg), &p); 04498 if (stream) 04499 stream->multicast_ttl = atoi(arg); 04500 } else if (!strcasecmp(cmd, "NoLoop")) { 04501 if (stream) 04502 stream->loop = 0; 04503 } else if (!strcasecmp(cmd, "</Stream>")) { 04504 if (!stream) { 04505 ERROR("No corresponding <Stream> for </Stream>\n"); 04506 } else { 04507 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) { 04508 if (audio_id != CODEC_ID_NONE) { 04509 audio_enc.codec_type = AVMEDIA_TYPE_AUDIO; 04510 audio_enc.codec_id = audio_id; 04511 add_codec(stream, &audio_enc); 04512 } 04513 if (video_id != CODEC_ID_NONE) { 04514 video_enc.codec_type = AVMEDIA_TYPE_VIDEO; 04515 video_enc.codec_id = video_id; 04516 add_codec(stream, &video_enc); 04517 } 04518 } 04519 stream = NULL; 04520 } 04521 } else if (!strcasecmp(cmd, "<Redirect")) { 04522 /*********************************************/ 04523 char *q; 04524 if (stream || feed || redirect) { 04525 ERROR("Already in a tag\n"); 04526 } else { 04527 redirect = av_mallocz(sizeof(FFStream)); 04528 *last_stream = redirect; 04529 last_stream = &redirect->next; 04530 04531 get_arg(redirect->filename, sizeof(redirect->filename), &p); 04532 q = strrchr(redirect->filename, '>'); 04533 if (*q) 04534 *q = '\0'; 04535 redirect->stream_type = STREAM_TYPE_REDIRECT; 04536 } 04537 } else if (!strcasecmp(cmd, "URL")) { 04538 if (redirect) 04539 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p); 04540 } else if (!strcasecmp(cmd, "</Redirect>")) { 04541 if (!redirect) { 04542 ERROR("No corresponding <Redirect> for </Redirect>\n"); 04543 } else { 04544 if (!redirect->feed_filename[0]) { 04545 ERROR("No URL found for <Redirect>\n"); 04546 } 04547 redirect = NULL; 04548 } 04549 } else if (!strcasecmp(cmd, "LoadModule")) { 04550 get_arg(arg, sizeof(arg), &p); 04551 #if HAVE_DLOPEN 04552 load_module(arg); 04553 #else 04554 ERROR("Module support not compiled into this version: '%s'\n", arg); 04555 #endif 04556 } else { 04557 ERROR("Incorrect keyword: '%s'\n", cmd); 04558 } 04559 } 04560 #undef ERROR 04561 04562 fclose(f); 04563 if (errors) 04564 return -1; 04565 else 04566 return 0; 04567 } 04568 04569 static void handle_child_exit(int sig) 04570 { 04571 pid_t pid; 04572 int status; 04573 04574 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { 04575 FFStream *feed; 04576 04577 for (feed = first_feed; feed; feed = feed->next) { 04578 if (feed->pid == pid) { 04579 int uptime = time(0) - feed->pid_start; 04580 04581 feed->pid = 0; 04582 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime); 04583 04584 if (uptime < 30) 04585 /* Turn off any more restarts */ 04586 feed->child_argv = 0; 04587 } 04588 } 04589 } 04590 04591 need_to_start_children = 1; 04592 } 04593 04594 static void opt_debug(void) 04595 { 04596 ffserver_debug = 1; 04597 ffserver_daemon = 0; 04598 logfilename[0] = '-'; 04599 } 04600 04601 static void show_help(void) 04602 { 04603 printf("usage: ffserver [options]\n" 04604 "Hyper fast multi format Audio/Video streaming server\n"); 04605 printf("\n"); 04606 show_help_options(options, "Main options:\n", 0, 0); 04607 } 04608 04609 static const OptionDef options[] = { 04610 #include "cmdutils_common_opts.h" 04611 { "n", OPT_BOOL, {(void *)&no_launch }, "enable no-launch mode" }, 04612 { "d", 0, {(void*)opt_debug}, "enable debug mode" }, 04613 { "f", HAS_ARG | OPT_STRING, {(void*)&config_filename }, "use configfile instead of /etc/ffserver.conf", "configfile" }, 04614 { NULL }, 04615 }; 04616 04617 int main(int argc, char **argv) 04618 { 04619 struct sigaction sigact; 04620 04621 av_register_all(); 04622 04623 show_banner(); 04624 04625 my_program_name = argv[0]; 04626 my_program_dir = getcwd(0, 0); 04627 ffserver_daemon = 1; 04628 04629 parse_options(argc, argv, options, NULL); 04630 04631 unsetenv("http_proxy"); /* Kill the http_proxy */ 04632 04633 av_lfg_init(&random_state, ff_random_get_seed()); 04634 04635 memset(&sigact, 0, sizeof(sigact)); 04636 sigact.sa_handler = handle_child_exit; 04637 sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART; 04638 sigaction(SIGCHLD, &sigact, 0); 04639 04640 if (parse_ffconfig(config_filename) < 0) { 04641 fprintf(stderr, "Incorrect config file - exiting.\n"); 04642 exit(1); 04643 } 04644 04645 /* open log file if needed */ 04646 if (logfilename[0] != '\0') { 04647 if (!strcmp(logfilename, "-")) 04648 logfile = stdout; 04649 else 04650 logfile = fopen(logfilename, "a"); 04651 av_log_set_callback(http_av_log); 04652 } 04653 04654 build_file_streams(); 04655 04656 build_feed_streams(); 04657 04658 compute_bandwidth(); 04659 04660 /* put the process in background and detach it from its TTY */ 04661 if (ffserver_daemon) { 04662 int pid; 04663 04664 pid = fork(); 04665 if (pid < 0) { 04666 perror("fork"); 04667 exit(1); 04668 } else if (pid > 0) { 04669 /* parent : exit */ 04670 exit(0); 04671 } else { 04672 /* child */ 04673 setsid(); 04674 close(0); 04675 open("/dev/null", O_RDWR); 04676 if (strcmp(logfilename, "-") != 0) { 04677 close(1); 04678 dup(0); 04679 } 04680 close(2); 04681 dup(0); 04682 } 04683 } 04684 04685 /* signal init */ 04686 signal(SIGPIPE, SIG_IGN); 04687 04688 if (ffserver_daemon) 04689 chdir("/"); 04690 04691 if (http_server() < 0) { 04692 http_log("Could not start server\n"); 04693 exit(1); 04694 } 04695 04696 return 0; 04697 }