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