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