blob: 8b819b693461772cc5da89cae2ae9c3ebbb68194
1 | /* |
2 | * Copyright (c) 2000, 2001, 2002 Fabrice Bellard |
3 | * |
4 | * This file is part of FFmpeg. |
5 | * |
6 | * FFmpeg is free software; you can redistribute it and/or |
7 | * modify it under the terms of the GNU Lesser General Public |
8 | * License as published by the Free Software Foundation; either |
9 | * version 2.1 of the License, or (at your option) any later version. |
10 | * |
11 | * FFmpeg is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | * Lesser General Public License for more details. |
15 | * |
16 | * You should have received a copy of the GNU Lesser General Public |
17 | * License along with FFmpeg; if not, write to the Free Software |
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
19 | */ |
20 | |
21 | /** |
22 | * @file |
23 | * multiple format streaming server based on the FFmpeg libraries |
24 | */ |
25 | |
26 | #include "config.h" |
27 | #if !HAVE_CLOSESOCKET |
28 | #define closesocket close |
29 | #endif |
30 | #include <string.h> |
31 | #include <stdlib.h> |
32 | #include <stdio.h> |
33 | #include "libavformat/avformat.h" |
34 | /* FIXME: those are internal headers, ffserver _really_ shouldn't use them */ |
35 | #include "libavformat/rtpproto.h" |
36 | #include "libavformat/rtsp.h" |
37 | #include "libavformat/avio_internal.h" |
38 | #include "libavformat/internal.h" |
39 | |
40 | #include "libavutil/avassert.h" |
41 | #include "libavutil/avstring.h" |
42 | #include "libavutil/lfg.h" |
43 | #include "libavutil/dict.h" |
44 | #include "libavutil/intreadwrite.h" |
45 | #include "libavutil/mathematics.h" |
46 | #include "libavutil/random_seed.h" |
47 | #include "libavutil/rational.h" |
48 | #include "libavutil/parseutils.h" |
49 | #include "libavutil/opt.h" |
50 | #include "libavutil/time.h" |
51 | |
52 | #include <stdarg.h> |
53 | #if HAVE_UNISTD_H |
54 | #include <unistd.h> |
55 | #endif |
56 | #include <fcntl.h> |
57 | #include <sys/ioctl.h> |
58 | #if HAVE_POLL_H |
59 | #include <poll.h> |
60 | #endif |
61 | #include <errno.h> |
62 | #include <time.h> |
63 | #include <sys/wait.h> |
64 | #include <signal.h> |
65 | |
66 | #include "cmdutils.h" |
67 | #include "ffserver_config.h" |
68 | |
69 | #define PATH_LENGTH 1024 |
70 | |
71 | const char program_name[] = "ffserver"; |
72 | const int program_birth_year = 2000; |
73 | |
74 | static const OptionDef options[]; |
75 | |
76 | enum HTTPState { |
77 | HTTPSTATE_WAIT_REQUEST, |
78 | HTTPSTATE_SEND_HEADER, |
79 | HTTPSTATE_SEND_DATA_HEADER, |
80 | HTTPSTATE_SEND_DATA, /* sending TCP or UDP data */ |
81 | HTTPSTATE_SEND_DATA_TRAILER, |
82 | HTTPSTATE_RECEIVE_DATA, |
83 | HTTPSTATE_WAIT_FEED, /* wait for data from the feed */ |
84 | HTTPSTATE_READY, |
85 | |
86 | RTSPSTATE_WAIT_REQUEST, |
87 | RTSPSTATE_SEND_REPLY, |
88 | RTSPSTATE_SEND_PACKET, |
89 | }; |
90 | |
91 | static const char * const http_state[] = { |
92 | "HTTP_WAIT_REQUEST", |
93 | "HTTP_SEND_HEADER", |
94 | |
95 | "SEND_DATA_HEADER", |
96 | "SEND_DATA", |
97 | "SEND_DATA_TRAILER", |
98 | "RECEIVE_DATA", |
99 | "WAIT_FEED", |
100 | "READY", |
101 | |
102 | "RTSP_WAIT_REQUEST", |
103 | "RTSP_SEND_REPLY", |
104 | "RTSP_SEND_PACKET", |
105 | }; |
106 | |
107 | #define IOBUFFER_INIT_SIZE 8192 |
108 | |
109 | /* timeouts are in ms */ |
110 | #define HTTP_REQUEST_TIMEOUT (15 * 1000) |
111 | #define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000) |
112 | |
113 | #define SYNC_TIMEOUT (10 * 1000) |
114 | |
115 | typedef struct RTSPActionServerSetup { |
116 | uint32_t ipaddr; |
117 | char transport_option[512]; |
118 | } RTSPActionServerSetup; |
119 | |
120 | typedef struct { |
121 | int64_t count1, count2; |
122 | int64_t time1, time2; |
123 | } DataRateData; |
124 | |
125 | /* context associated with one connection */ |
126 | typedef struct HTTPContext { |
127 | enum HTTPState state; |
128 | int fd; /* socket file descriptor */ |
129 | struct sockaddr_in from_addr; /* origin */ |
130 | struct pollfd *poll_entry; /* used when polling */ |
131 | int64_t timeout; |
132 | uint8_t *buffer_ptr, *buffer_end; |
133 | int http_error; |
134 | int post; |
135 | int chunked_encoding; |
136 | int chunk_size; /* 0 if it needs to be read */ |
137 | struct HTTPContext *next; |
138 | int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */ |
139 | int64_t data_count; |
140 | /* feed input */ |
141 | int feed_fd; |
142 | /* input format handling */ |
143 | AVFormatContext *fmt_in; |
144 | int64_t start_time; /* In milliseconds - this wraps fairly often */ |
145 | int64_t first_pts; /* initial pts value */ |
146 | int64_t cur_pts; /* current pts value from the stream in us */ |
147 | int64_t cur_frame_duration; /* duration of the current frame in us */ |
148 | int cur_frame_bytes; /* output frame size, needed to compute |
149 | the time at which we send each |
150 | packet */ |
151 | int pts_stream_index; /* stream we choose as clock reference */ |
152 | int64_t cur_clock; /* current clock reference value in us */ |
153 | /* output format handling */ |
154 | struct FFServerStream *stream; |
155 | /* -1 is invalid stream */ |
156 | int feed_streams[FFSERVER_MAX_STREAMS]; /* index of streams in the feed */ |
157 | int switch_feed_streams[FFSERVER_MAX_STREAMS]; /* index of streams in the feed */ |
158 | int switch_pending; |
159 | AVFormatContext *pfmt_ctx; /* instance of FFServerStream for one user */ |
160 | int last_packet_sent; /* true if last data packet was sent */ |
161 | int suppress_log; |
162 | DataRateData datarate; |
163 | int wmp_client_id; |
164 | char protocol[16]; |
165 | char method[16]; |
166 | char url[128]; |
167 | char clean_url[128*7]; |
168 | int buffer_size; |
169 | uint8_t *buffer; |
170 | int is_packetized; /* if true, the stream is packetized */ |
171 | int packet_stream_index; /* current stream for output in state machine */ |
172 | |
173 | /* RTSP state specific */ |
174 | uint8_t *pb_buffer; /* XXX: use that in all the code */ |
175 | AVIOContext *pb; |
176 | int seq; /* RTSP sequence number */ |
177 | |
178 | /* RTP state specific */ |
179 | enum RTSPLowerTransport rtp_protocol; |
180 | char session_id[32]; /* session id */ |
181 | AVFormatContext *rtp_ctx[FFSERVER_MAX_STREAMS]; |
182 | |
183 | /* RTP/UDP specific */ |
184 | URLContext *rtp_handles[FFSERVER_MAX_STREAMS]; |
185 | |
186 | /* RTP/TCP specific */ |
187 | struct HTTPContext *rtsp_c; |
188 | uint8_t *packet_buffer, *packet_buffer_ptr, *packet_buffer_end; |
189 | } HTTPContext; |
190 | |
191 | static HTTPContext *first_http_ctx; |
192 | |
193 | static FFServerConfig config = { |
194 | .nb_max_http_connections = 2000, |
195 | .nb_max_connections = 5, |
196 | .max_bandwidth = 1000, |
197 | .use_defaults = 1, |
198 | }; |
199 | |
200 | static void new_connection(int server_fd, int is_rtsp); |
201 | static void close_connection(HTTPContext *c); |
202 | |
203 | /* HTTP handling */ |
204 | static int handle_connection(HTTPContext *c); |
205 | static inline void print_stream_params(AVIOContext *pb, FFServerStream *stream); |
206 | static void compute_status(HTTPContext *c); |
207 | static int open_input_stream(HTTPContext *c, const char *info); |
208 | static int http_parse_request(HTTPContext *c); |
209 | static int http_send_data(HTTPContext *c); |
210 | static int http_start_receive_data(HTTPContext *c); |
211 | static int http_receive_data(HTTPContext *c); |
212 | |
213 | /* RTSP handling */ |
214 | static int rtsp_parse_request(HTTPContext *c); |
215 | static void rtsp_cmd_describe(HTTPContext *c, const char *url); |
216 | static void rtsp_cmd_options(HTTPContext *c, const char *url); |
217 | static void rtsp_cmd_setup(HTTPContext *c, const char *url, |
218 | RTSPMessageHeader *h); |
219 | static void rtsp_cmd_play(HTTPContext *c, const char *url, |
220 | RTSPMessageHeader *h); |
221 | static void rtsp_cmd_interrupt(HTTPContext *c, const char *url, |
222 | RTSPMessageHeader *h, int pause_only); |
223 | |
224 | /* SDP handling */ |
225 | static int prepare_sdp_description(FFServerStream *stream, uint8_t **pbuffer, |
226 | struct in_addr my_ip); |
227 | |
228 | /* RTP handling */ |
229 | static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr, |
230 | FFServerStream *stream, |
231 | const char *session_id, |
232 | enum RTSPLowerTransport rtp_protocol); |
233 | static int rtp_new_av_stream(HTTPContext *c, |
234 | int stream_index, struct sockaddr_in *dest_addr, |
235 | HTTPContext *rtsp_c); |
236 | /* utils */ |
237 | static size_t htmlencode (const char *src, char **dest); |
238 | static inline void cp_html_entity (char *buffer, const char *entity); |
239 | static inline int check_codec_match(LayeredAVStream *ccf, AVStream *ccs, int stream); |
240 | |
241 | static const char *my_program_name; |
242 | |
243 | static int no_launch; |
244 | static int need_to_start_children; |
245 | |
246 | /* maximum number of simultaneous HTTP connections */ |
247 | static unsigned int nb_connections; |
248 | |
249 | static uint64_t current_bandwidth; |
250 | |
251 | /* Making this global saves on passing it around everywhere */ |
252 | static int64_t cur_time; |
253 | |
254 | static AVLFG random_state; |
255 | |
256 | static FILE *logfile = NULL; |
257 | |
258 | static void unlayer_stream(AVStream *st, LayeredAVStream *lst) |
259 | { |
260 | avcodec_free_context(&st->codec); |
261 | avcodec_parameters_free(&st->codecpar); |
262 | #define COPY(a) st->a = lst->a; |
263 | COPY(index) |
264 | COPY(id) |
265 | COPY(codec) |
266 | COPY(codecpar) |
267 | COPY(time_base) |
268 | COPY(pts_wrap_bits) |
269 | COPY(sample_aspect_ratio) |
270 | COPY(recommended_encoder_configuration) |
271 | } |
272 | |
273 | static inline void cp_html_entity (char *buffer, const char *entity) { |
274 | if (!buffer || !entity) |
275 | return; |
276 | while (*entity) |
277 | *buffer++ = *entity++; |
278 | } |
279 | |
280 | /** |
281 | * Substitutes known conflicting chars on a text string with |
282 | * their corresponding HTML entities. |
283 | * |
284 | * Returns the number of bytes in the 'encoded' representation |
285 | * not including the terminating NUL. |
286 | */ |
287 | static size_t htmlencode (const char *src, char **dest) { |
288 | const char *amp = "&"; |
289 | const char *lt = "<"; |
290 | const char *gt = ">"; |
291 | const char *start; |
292 | char *tmp; |
293 | size_t final_size = 0; |
294 | |
295 | if (!src) |
296 | return 0; |
297 | |
298 | start = src; |
299 | |
300 | /* Compute needed dest size */ |
301 | while (*src != '\0') { |
302 | switch(*src) { |
303 | case 38: /* & */ |
304 | final_size += 5; |
305 | break; |
306 | case 60: /* < */ |
307 | case 62: /* > */ |
308 | final_size += 4; |
309 | break; |
310 | default: |
311 | final_size++; |
312 | } |
313 | src++; |
314 | } |
315 | |
316 | src = start; |
317 | *dest = av_mallocz(final_size + 1); |
318 | if (!*dest) |
319 | return 0; |
320 | |
321 | /* Build dest */ |
322 | tmp = *dest; |
323 | while (*src != '\0') { |
324 | switch(*src) { |
325 | case 38: /* & */ |
326 | cp_html_entity (tmp, amp); |
327 | tmp += 5; |
328 | break; |
329 | case 60: /* < */ |
330 | cp_html_entity (tmp, lt); |
331 | tmp += 4; |
332 | break; |
333 | case 62: /* > */ |
334 | cp_html_entity (tmp, gt); |
335 | tmp += 4; |
336 | break; |
337 | default: |
338 | *tmp = *src; |
339 | tmp += 1; |
340 | } |
341 | src++; |
342 | } |
343 | *tmp = '\0'; |
344 | |
345 | return final_size; |
346 | } |
347 | |
348 | static int64_t ffm_read_write_index(int fd) |
349 | { |
350 | uint8_t buf[8]; |
351 | |
352 | if (lseek(fd, 8, SEEK_SET) < 0) |
353 | return AVERROR(EIO); |
354 | if (read(fd, buf, 8) != 8) |
355 | return AVERROR(EIO); |
356 | return AV_RB64(buf); |
357 | } |
358 | |
359 | static int ffm_write_write_index(int fd, int64_t pos) |
360 | { |
361 | uint8_t buf[8]; |
362 | int i; |
363 | |
364 | for(i=0;i<8;i++) |
365 | buf[i] = (pos >> (56 - i * 8)) & 0xff; |
366 | if (lseek(fd, 8, SEEK_SET) < 0) |
367 | goto bail_eio; |
368 | if (write(fd, buf, 8) != 8) |
369 | goto bail_eio; |
370 | |
371 | return 8; |
372 | |
373 | bail_eio: |
374 | return AVERROR(EIO); |
375 | } |
376 | |
377 | static void ffm_set_write_index(AVFormatContext *s, int64_t pos, |
378 | int64_t file_size) |
379 | { |
380 | av_opt_set_int(s, "server_attached", 1, AV_OPT_SEARCH_CHILDREN); |
381 | av_opt_set_int(s, "ffm_write_index", pos, AV_OPT_SEARCH_CHILDREN); |
382 | av_opt_set_int(s, "ffm_file_size", file_size, AV_OPT_SEARCH_CHILDREN); |
383 | } |
384 | |
385 | static char *ctime1(char *buf2, size_t buf_size) |
386 | { |
387 | time_t ti; |
388 | char *p; |
389 | |
390 | ti = time(NULL); |
391 | p = ctime(&ti); |
392 | if (!p || !*p) { |
393 | *buf2 = '\0'; |
394 | return buf2; |
395 | } |
396 | av_strlcpy(buf2, p, buf_size); |
397 | p = buf2 + strlen(buf2) - 1; |
398 | if (*p == '\n') |
399 | *p = '\0'; |
400 | return buf2; |
401 | } |
402 | |
403 | static void http_vlog(const char *fmt, va_list vargs) |
404 | { |
405 | static int print_prefix = 1; |
406 | char buf[32]; |
407 | |
408 | if (!logfile) |
409 | return; |
410 | |
411 | if (print_prefix) { |
412 | ctime1(buf, sizeof(buf)); |
413 | fprintf(logfile, "%s ", buf); |
414 | } |
415 | print_prefix = strstr(fmt, "\n") != NULL; |
416 | vfprintf(logfile, fmt, vargs); |
417 | fflush(logfile); |
418 | } |
419 | |
420 | #ifdef __GNUC__ |
421 | __attribute__ ((format (printf, 1, 2))) |
422 | #endif |
423 | static void http_log(const char *fmt, ...) |
424 | { |
425 | va_list vargs; |
426 | va_start(vargs, fmt); |
427 | http_vlog(fmt, vargs); |
428 | va_end(vargs); |
429 | } |
430 | |
431 | static void http_av_log(void *ptr, int level, const char *fmt, va_list vargs) |
432 | { |
433 | static int print_prefix = 1; |
434 | AVClass *avc = ptr ? *(AVClass**)ptr : NULL; |
435 | if (level > av_log_get_level()) |
436 | return; |
437 | if (print_prefix && avc) |
438 | http_log("[%s @ %p]", avc->item_name(ptr), ptr); |
439 | print_prefix = strstr(fmt, "\n") != NULL; |
440 | http_vlog(fmt, vargs); |
441 | } |
442 | |
443 | static void log_connection(HTTPContext *c) |
444 | { |
445 | if (c->suppress_log) |
446 | return; |
447 | |
448 | http_log("%s - - [%s] \"%s %s\" %d %"PRId64"\n", |
449 | inet_ntoa(c->from_addr.sin_addr), c->method, c->url, |
450 | c->protocol, (c->http_error ? c->http_error : 200), c->data_count); |
451 | } |
452 | |
453 | static void update_datarate(DataRateData *drd, int64_t count) |
454 | { |
455 | if (!drd->time1 && !drd->count1) { |
456 | drd->time1 = drd->time2 = cur_time; |
457 | drd->count1 = drd->count2 = count; |
458 | } else if (cur_time - drd->time2 > 5000) { |
459 | drd->time1 = drd->time2; |
460 | drd->count1 = drd->count2; |
461 | drd->time2 = cur_time; |
462 | drd->count2 = count; |
463 | } |
464 | } |
465 | |
466 | /* In bytes per second */ |
467 | static int compute_datarate(DataRateData *drd, int64_t count) |
468 | { |
469 | if (cur_time == drd->time1) |
470 | return 0; |
471 | |
472 | return ((count - drd->count1) * 1000) / (cur_time - drd->time1); |
473 | } |
474 | |
475 | |
476 | static void start_children(FFServerStream *feed) |
477 | { |
478 | char *pathname; |
479 | char *slash; |
480 | int i; |
481 | size_t cmd_length; |
482 | |
483 | if (no_launch) |
484 | return; |
485 | |
486 | cmd_length = strlen(my_program_name); |
487 | |
488 | /** |
489 | * FIXME: WIP Safeguard. Remove after clearing all harcoded |
490 | * '1024' path lengths |
491 | */ |
492 | if (cmd_length > PATH_LENGTH - 1) { |
493 | http_log("Could not start children. Command line: '%s' exceeds " |
494 | "path length limit (%d)\n", my_program_name, PATH_LENGTH); |
495 | return; |
496 | } |
497 | |
498 | slash = strrchr(my_program_name, '/'); |
499 | if (!slash) { |
500 | pathname = av_mallocz(sizeof("ffmpeg")); |
501 | } else { |
502 | pathname = av_mallocz(slash - my_program_name + sizeof("ffmpeg")); |
503 | if (pathname != NULL) { |
504 | memcpy(pathname, my_program_name, slash - my_program_name); |
505 | } |
506 | } |
507 | if (!pathname) { |
508 | http_log("Could not allocate memory for children cmd line\n"); |
509 | return; |
510 | } |
511 | /* use "ffmpeg" in the path of current program. Ignore user provided path */ |
512 | |
513 | strcat(pathname, "ffmpeg"); |
514 | |
515 | for (; feed; feed = feed->next) { |
516 | |
517 | if (!feed->child_argv || feed->pid) |
518 | continue; |
519 | |
520 | feed->pid_start = time(0); |
521 | |
522 | feed->pid = fork(); |
523 | if (feed->pid < 0) { |
524 | http_log("Unable to create children: %s\n", strerror(errno)); |
525 | av_free (pathname); |
526 | exit(EXIT_FAILURE); |
527 | } |
528 | |
529 | if (feed->pid) |
530 | continue; |
531 | |
532 | /* In child */ |
533 | |
534 | http_log("Launch command line: "); |
535 | http_log("%s ", pathname); |
536 | |
537 | for (i = 1; feed->child_argv[i] && feed->child_argv[i][0]; i++) |
538 | http_log("%s ", feed->child_argv[i]); |
539 | http_log("\n"); |
540 | |
541 | for (i = 3; i < 256; i++) |
542 | close(i); |
543 | |
544 | if (!config.debug) { |
545 | if (!freopen("/dev/null", "r", stdin)) |
546 | http_log("failed to redirect STDIN to /dev/null\n;"); |
547 | if (!freopen("/dev/null", "w", stdout)) |
548 | http_log("failed to redirect STDOUT to /dev/null\n;"); |
549 | if (!freopen("/dev/null", "w", stderr)) |
550 | http_log("failed to redirect STDERR to /dev/null\n;"); |
551 | } |
552 | |
553 | signal(SIGPIPE, SIG_DFL); |
554 | execvp(pathname, feed->child_argv); |
555 | av_free (pathname); |
556 | _exit(1); |
557 | } |
558 | av_free (pathname); |
559 | } |
560 | |
561 | /* open a listening socket */ |
562 | static int socket_open_listen(struct sockaddr_in *my_addr) |
563 | { |
564 | int server_fd, tmp; |
565 | |
566 | server_fd = socket(AF_INET,SOCK_STREAM,0); |
567 | if (server_fd < 0) { |
568 | perror ("socket"); |
569 | return -1; |
570 | } |
571 | |
572 | tmp = 1; |
573 | if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp))) |
574 | av_log(NULL, AV_LOG_WARNING, "setsockopt SO_REUSEADDR failed\n"); |
575 | |
576 | my_addr->sin_family = AF_INET; |
577 | if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) { |
578 | char bindmsg[32]; |
579 | snprintf(bindmsg, sizeof(bindmsg), "bind(port %d)", |
580 | ntohs(my_addr->sin_port)); |
581 | perror (bindmsg); |
582 | goto fail; |
583 | } |
584 | |
585 | if (listen (server_fd, 5) < 0) { |
586 | perror ("listen"); |
587 | goto fail; |
588 | } |
589 | |
590 | if (ff_socket_nonblock(server_fd, 1) < 0) |
591 | av_log(NULL, AV_LOG_WARNING, "ff_socket_nonblock failed\n"); |
592 | |
593 | return server_fd; |
594 | |
595 | fail: |
596 | closesocket(server_fd); |
597 | return -1; |
598 | } |
599 | |
600 | /* start all multicast streams */ |
601 | static void start_multicast(void) |
602 | { |
603 | FFServerStream *stream; |
604 | char session_id[32]; |
605 | HTTPContext *rtp_c; |
606 | struct sockaddr_in dest_addr = {0}; |
607 | int default_port, stream_index; |
608 | unsigned int random0, random1; |
609 | |
610 | default_port = 6000; |
611 | for(stream = config.first_stream; stream; stream = stream->next) { |
612 | |
613 | if (!stream->is_multicast) |
614 | continue; |
615 | |
616 | random0 = av_lfg_get(&random_state); |
617 | random1 = av_lfg_get(&random_state); |
618 | |
619 | /* open the RTP connection */ |
620 | snprintf(session_id, sizeof(session_id), "%08x%08x", random0, random1); |
621 | |
622 | /* choose a port if none given */ |
623 | if (stream->multicast_port == 0) { |
624 | stream->multicast_port = default_port; |
625 | default_port += 100; |
626 | } |
627 | |
628 | dest_addr.sin_family = AF_INET; |
629 | dest_addr.sin_addr = stream->multicast_ip; |
630 | dest_addr.sin_port = htons(stream->multicast_port); |
631 | |
632 | rtp_c = rtp_new_connection(&dest_addr, stream, session_id, |
633 | RTSP_LOWER_TRANSPORT_UDP_MULTICAST); |
634 | if (!rtp_c) |
635 | continue; |
636 | |
637 | if (open_input_stream(rtp_c, "") < 0) { |
638 | http_log("Could not open input stream for stream '%s'\n", |
639 | stream->filename); |
640 | continue; |
641 | } |
642 | |
643 | /* open each RTP stream */ |
644 | for(stream_index = 0; stream_index < stream->nb_streams; |
645 | stream_index++) { |
646 | dest_addr.sin_port = htons(stream->multicast_port + |
647 | 2 * stream_index); |
648 | if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, NULL) >= 0) |
649 | continue; |
650 | |
651 | http_log("Could not open output stream '%s/streamid=%d'\n", |
652 | stream->filename, stream_index); |
653 | exit(1); |
654 | } |
655 | |
656 | rtp_c->state = HTTPSTATE_SEND_DATA; |
657 | } |
658 | } |
659 | |
660 | /* main loop of the HTTP server */ |
661 | static int http_server(void) |
662 | { |
663 | int server_fd = 0, rtsp_server_fd = 0; |
664 | int ret, delay; |
665 | struct pollfd *poll_table, *poll_entry; |
666 | HTTPContext *c, *c_next; |
667 | |
668 | poll_table = av_mallocz_array(config.nb_max_http_connections + 2, |
669 | sizeof(*poll_table)); |
670 | if(!poll_table) { |
671 | http_log("Impossible to allocate a poll table handling %d " |
672 | "connections.\n", config.nb_max_http_connections); |
673 | return -1; |
674 | } |
675 | |
676 | if (config.http_addr.sin_port) { |
677 | server_fd = socket_open_listen(&config.http_addr); |
678 | if (server_fd < 0) |
679 | goto quit; |
680 | } |
681 | |
682 | if (config.rtsp_addr.sin_port) { |
683 | rtsp_server_fd = socket_open_listen(&config.rtsp_addr); |
684 | if (rtsp_server_fd < 0) { |
685 | closesocket(server_fd); |
686 | goto quit; |
687 | } |
688 | } |
689 | |
690 | if (!rtsp_server_fd && !server_fd) { |
691 | http_log("HTTP and RTSP disabled.\n"); |
692 | goto quit; |
693 | } |
694 | |
695 | http_log("FFserver started.\n"); |
696 | |
697 | start_children(config.first_feed); |
698 | |
699 | start_multicast(); |
700 | |
701 | for(;;) { |
702 | poll_entry = poll_table; |
703 | if (server_fd) { |
704 | poll_entry->fd = server_fd; |
705 | poll_entry->events = POLLIN; |
706 | poll_entry++; |
707 | } |
708 | if (rtsp_server_fd) { |
709 | poll_entry->fd = rtsp_server_fd; |
710 | poll_entry->events = POLLIN; |
711 | poll_entry++; |
712 | } |
713 | |
714 | /* wait for events on each HTTP handle */ |
715 | c = first_http_ctx; |
716 | delay = 1000; |
717 | while (c) { |
718 | int fd; |
719 | fd = c->fd; |
720 | switch(c->state) { |
721 | case HTTPSTATE_SEND_HEADER: |
722 | case RTSPSTATE_SEND_REPLY: |
723 | case RTSPSTATE_SEND_PACKET: |
724 | c->poll_entry = poll_entry; |
725 | poll_entry->fd = fd; |
726 | poll_entry->events = POLLOUT; |
727 | poll_entry++; |
728 | break; |
729 | case HTTPSTATE_SEND_DATA_HEADER: |
730 | case HTTPSTATE_SEND_DATA: |
731 | case HTTPSTATE_SEND_DATA_TRAILER: |
732 | if (!c->is_packetized) { |
733 | /* for TCP, we output as much as we can |
734 | * (may need to put a limit) */ |
735 | c->poll_entry = poll_entry; |
736 | poll_entry->fd = fd; |
737 | poll_entry->events = POLLOUT; |
738 | poll_entry++; |
739 | } else { |
740 | /* when ffserver is doing the timing, we work by |
741 | * looking at which packet needs to be sent every |
742 | * 10 ms (one tick wait XXX: 10 ms assumed) */ |
743 | if (delay > 10) |
744 | delay = 10; |
745 | } |
746 | break; |
747 | case HTTPSTATE_WAIT_REQUEST: |
748 | case HTTPSTATE_RECEIVE_DATA: |
749 | case HTTPSTATE_WAIT_FEED: |
750 | case RTSPSTATE_WAIT_REQUEST: |
751 | /* need to catch errors */ |
752 | c->poll_entry = poll_entry; |
753 | poll_entry->fd = fd; |
754 | poll_entry->events = POLLIN;/* Maybe this will work */ |
755 | poll_entry++; |
756 | break; |
757 | default: |
758 | c->poll_entry = NULL; |
759 | break; |
760 | } |
761 | c = c->next; |
762 | } |
763 | |
764 | /* wait for an event on one connection. We poll at least every |
765 | * second to handle timeouts */ |
766 | do { |
767 | ret = poll(poll_table, poll_entry - poll_table, delay); |
768 | if (ret < 0 && ff_neterrno() != AVERROR(EAGAIN) && |
769 | ff_neterrno() != AVERROR(EINTR)) { |
770 | goto quit; |
771 | } |
772 | } while (ret < 0); |
773 | |
774 | cur_time = av_gettime() / 1000; |
775 | |
776 | if (need_to_start_children) { |
777 | need_to_start_children = 0; |
778 | start_children(config.first_feed); |
779 | } |
780 | |
781 | /* now handle the events */ |
782 | for(c = first_http_ctx; c; c = c_next) { |
783 | c_next = c->next; |
784 | if (handle_connection(c) < 0) { |
785 | log_connection(c); |
786 | /* close and free the connection */ |
787 | close_connection(c); |
788 | } |
789 | } |
790 | |
791 | poll_entry = poll_table; |
792 | if (server_fd) { |
793 | /* new HTTP connection request ? */ |
794 | if (poll_entry->revents & POLLIN) |
795 | new_connection(server_fd, 0); |
796 | poll_entry++; |
797 | } |
798 | if (rtsp_server_fd) { |
799 | /* new RTSP connection request ? */ |
800 | if (poll_entry->revents & POLLIN) |
801 | new_connection(rtsp_server_fd, 1); |
802 | } |
803 | } |
804 | |
805 | quit: |
806 | av_free(poll_table); |
807 | return -1; |
808 | } |
809 | |
810 | /* start waiting for a new HTTP/RTSP request */ |
811 | static void start_wait_request(HTTPContext *c, int is_rtsp) |
812 | { |
813 | c->buffer_ptr = c->buffer; |
814 | c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */ |
815 | |
816 | c->state = is_rtsp ? RTSPSTATE_WAIT_REQUEST : HTTPSTATE_WAIT_REQUEST; |
817 | c->timeout = cur_time + |
818 | (is_rtsp ? RTSP_REQUEST_TIMEOUT : HTTP_REQUEST_TIMEOUT); |
819 | } |
820 | |
821 | static void http_send_too_busy_reply(int fd) |
822 | { |
823 | char buffer[400]; |
824 | int len = snprintf(buffer, sizeof(buffer), |
825 | "HTTP/1.0 503 Server too busy\r\n" |
826 | "Content-type: text/html\r\n" |
827 | "\r\n" |
828 | "<!DOCTYPE html>\n" |
829 | "<html><head><title>Too busy</title></head><body>\r\n" |
830 | "<p>The server is too busy to serve your request at " |
831 | "this time.</p>\r\n" |
832 | "<p>The number of current connections is %u, and this " |
833 | "exceeds the limit of %u.</p>\r\n" |
834 | "</body></html>\r\n", |
835 | nb_connections, config.nb_max_connections); |
836 | av_assert0(len < sizeof(buffer)); |
837 | if (send(fd, buffer, len, 0) < len) |
838 | av_log(NULL, AV_LOG_WARNING, |
839 | "Could not send too-busy reply, send() failed\n"); |
840 | } |
841 | |
842 | |
843 | static void new_connection(int server_fd, int is_rtsp) |
844 | { |
845 | struct sockaddr_in from_addr; |
846 | socklen_t len; |
847 | int fd; |
848 | HTTPContext *c = NULL; |
849 | |
850 | len = sizeof(from_addr); |
851 | fd = accept(server_fd, (struct sockaddr *)&from_addr, |
852 | &len); |
853 | if (fd < 0) { |
854 | http_log("error during accept %s\n", strerror(errno)); |
855 | return; |
856 | } |
857 | if (ff_socket_nonblock(fd, 1) < 0) |
858 | av_log(NULL, AV_LOG_WARNING, "ff_socket_nonblock failed\n"); |
859 | |
860 | if (nb_connections >= config.nb_max_connections) { |
861 | http_send_too_busy_reply(fd); |
862 | goto fail; |
863 | } |
864 | |
865 | /* add a new connection */ |
866 | c = av_mallocz(sizeof(HTTPContext)); |
867 | if (!c) |
868 | goto fail; |
869 | |
870 | c->fd = fd; |
871 | c->poll_entry = NULL; |
872 | c->from_addr = from_addr; |
873 | c->buffer_size = IOBUFFER_INIT_SIZE; |
874 | c->buffer = av_malloc(c->buffer_size); |
875 | if (!c->buffer) |
876 | goto fail; |
877 | |
878 | c->next = first_http_ctx; |
879 | first_http_ctx = c; |
880 | nb_connections++; |
881 | |
882 | start_wait_request(c, is_rtsp); |
883 | |
884 | return; |
885 | |
886 | fail: |
887 | if (c) { |
888 | av_freep(&c->buffer); |
889 | av_free(c); |
890 | } |
891 | closesocket(fd); |
892 | } |
893 | |
894 | static void close_connection(HTTPContext *c) |
895 | { |
896 | HTTPContext **cp, *c1; |
897 | int i, nb_streams; |
898 | AVFormatContext *ctx; |
899 | AVStream *st; |
900 | |
901 | /* remove connection from list */ |
902 | cp = &first_http_ctx; |
903 | while (*cp) { |
904 | c1 = *cp; |
905 | if (c1 == c) |
906 | *cp = c->next; |
907 | else |
908 | cp = &c1->next; |
909 | } |
910 | |
911 | /* remove references, if any (XXX: do it faster) */ |
912 | for(c1 = first_http_ctx; c1; c1 = c1->next) { |
913 | if (c1->rtsp_c == c) |
914 | c1->rtsp_c = NULL; |
915 | } |
916 | |
917 | /* remove connection associated resources */ |
918 | if (c->fd >= 0) |
919 | closesocket(c->fd); |
920 | if (c->fmt_in) { |
921 | /* close each frame parser */ |
922 | for(i=0;i<c->fmt_in->nb_streams;i++) { |
923 | st = c->fmt_in->streams[i]; |
924 | if (st->codec->codec) |
925 | avcodec_close(st->codec); |
926 | } |
927 | avformat_close_input(&c->fmt_in); |
928 | } |
929 | |
930 | /* free RTP output streams if any */ |
931 | nb_streams = 0; |
932 | if (c->stream) |
933 | nb_streams = c->stream->nb_streams; |
934 | |
935 | for(i=0;i<nb_streams;i++) { |
936 | ctx = c->rtp_ctx[i]; |
937 | if (ctx) { |
938 | av_write_trailer(ctx); |
939 | av_dict_free(&ctx->metadata); |
940 | av_freep(&ctx->streams[0]); |
941 | av_freep(&ctx); |
942 | } |
943 | ffurl_close(c->rtp_handles[i]); |
944 | } |
945 | |
946 | ctx = c->pfmt_ctx; |
947 | |
948 | if (ctx) { |
949 | if (!c->last_packet_sent && c->state == HTTPSTATE_SEND_DATA_TRAILER) { |
950 | /* prepare header */ |
951 | if (ctx->oformat && avio_open_dyn_buf(&ctx->pb) >= 0) { |
952 | av_write_trailer(ctx); |
953 | av_freep(&c->pb_buffer); |
954 | avio_close_dyn_buf(ctx->pb, &c->pb_buffer); |
955 | } |
956 | } |
957 | for(i=0; i<ctx->nb_streams; i++) |
958 | av_freep(&ctx->streams[i]); |
959 | av_freep(&ctx->streams); |
960 | av_freep(&ctx->priv_data); |
961 | } |
962 | |
963 | if (c->stream && !c->post && c->stream->stream_type == STREAM_TYPE_LIVE) |
964 | current_bandwidth -= c->stream->bandwidth; |
965 | |
966 | /* signal that there is no feed if we are the feeder socket */ |
967 | if (c->state == HTTPSTATE_RECEIVE_DATA && c->stream) { |
968 | c->stream->feed_opened = 0; |
969 | close(c->feed_fd); |
970 | } |
971 | |
972 | av_freep(&c->pb_buffer); |
973 | av_freep(&c->packet_buffer); |
974 | av_freep(&c->buffer); |
975 | av_free(c); |
976 | nb_connections--; |
977 | } |
978 | |
979 | static int handle_connection(HTTPContext *c) |
980 | { |
981 | int len, ret; |
982 | uint8_t *ptr; |
983 | |
984 | switch(c->state) { |
985 | case HTTPSTATE_WAIT_REQUEST: |
986 | case RTSPSTATE_WAIT_REQUEST: |
987 | /* timeout ? */ |
988 | if ((c->timeout - cur_time) < 0) |
989 | return -1; |
990 | if (c->poll_entry->revents & (POLLERR | POLLHUP)) |
991 | return -1; |
992 | |
993 | /* no need to read if no events */ |
994 | if (!(c->poll_entry->revents & POLLIN)) |
995 | return 0; |
996 | /* read the data */ |
997 | read_loop: |
998 | if (!(len = recv(c->fd, c->buffer_ptr, 1, 0))) |
999 | return -1; |
1000 | |
1001 | if (len < 0) { |
1002 | if (ff_neterrno() != AVERROR(EAGAIN) && |
1003 | ff_neterrno() != AVERROR(EINTR)) |
1004 | return -1; |
1005 | break; |
1006 | } |
1007 | /* search for end of request. */ |
1008 | c->buffer_ptr += len; |
1009 | ptr = c->buffer_ptr; |
1010 | if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) || |
1011 | (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) { |
1012 | /* request found : parse it and reply */ |
1013 | if (c->state == HTTPSTATE_WAIT_REQUEST) |
1014 | ret = http_parse_request(c); |
1015 | else |
1016 | ret = rtsp_parse_request(c); |
1017 | |
1018 | if (ret < 0) |
1019 | return -1; |
1020 | } else if (ptr >= c->buffer_end) { |
1021 | /* request too long: cannot do anything */ |
1022 | return -1; |
1023 | } else goto read_loop; |
1024 | |
1025 | break; |
1026 | |
1027 | case HTTPSTATE_SEND_HEADER: |
1028 | if (c->poll_entry->revents & (POLLERR | POLLHUP)) |
1029 | return -1; |
1030 | |
1031 | /* no need to write if no events */ |
1032 | if (!(c->poll_entry->revents & POLLOUT)) |
1033 | return 0; |
1034 | len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0); |
1035 | if (len < 0) { |
1036 | if (ff_neterrno() != AVERROR(EAGAIN) && |
1037 | ff_neterrno() != AVERROR(EINTR)) { |
1038 | goto close_connection; |
1039 | } |
1040 | break; |
1041 | } |
1042 | c->buffer_ptr += len; |
1043 | if (c->stream) |
1044 | c->stream->bytes_served += len; |
1045 | c->data_count += len; |
1046 | if (c->buffer_ptr >= c->buffer_end) { |
1047 | av_freep(&c->pb_buffer); |
1048 | /* if error, exit */ |
1049 | if (c->http_error) |
1050 | return -1; |
1051 | /* all the buffer was sent : synchronize to the incoming |
1052 | * stream */ |
1053 | c->state = HTTPSTATE_SEND_DATA_HEADER; |
1054 | c->buffer_ptr = c->buffer_end = c->buffer; |
1055 | } |
1056 | break; |
1057 | |
1058 | case HTTPSTATE_SEND_DATA: |
1059 | case HTTPSTATE_SEND_DATA_HEADER: |
1060 | case HTTPSTATE_SEND_DATA_TRAILER: |
1061 | /* for packetized output, we consider we can always write (the |
1062 | * input streams set the speed). It may be better to verify |
1063 | * that we do not rely too much on the kernel queues */ |
1064 | if (!c->is_packetized) { |
1065 | if (c->poll_entry->revents & (POLLERR | POLLHUP)) |
1066 | return -1; |
1067 | |
1068 | /* no need to read if no events */ |
1069 | if (!(c->poll_entry->revents & POLLOUT)) |
1070 | return 0; |
1071 | } |
1072 | if (http_send_data(c) < 0) |
1073 | return -1; |
1074 | /* close connection if trailer sent */ |
1075 | if (c->state == HTTPSTATE_SEND_DATA_TRAILER) |
1076 | return -1; |
1077 | /* Check if it is a single jpeg frame 123 */ |
1078 | if (c->stream->single_frame && c->data_count > c->cur_frame_bytes && c->cur_frame_bytes > 0) { |
1079 | close_connection(c); |
1080 | } |
1081 | break; |
1082 | case HTTPSTATE_RECEIVE_DATA: |
1083 | /* no need to read if no events */ |
1084 | if (c->poll_entry->revents & (POLLERR | POLLHUP)) |
1085 | return -1; |
1086 | if (!(c->poll_entry->revents & POLLIN)) |
1087 | return 0; |
1088 | if (http_receive_data(c) < 0) |
1089 | return -1; |
1090 | break; |
1091 | case HTTPSTATE_WAIT_FEED: |
1092 | /* no need to read if no events */ |
1093 | if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP)) |
1094 | return -1; |
1095 | |
1096 | /* nothing to do, we'll be waken up by incoming feed packets */ |
1097 | break; |
1098 | |
1099 | case RTSPSTATE_SEND_REPLY: |
1100 | if (c->poll_entry->revents & (POLLERR | POLLHUP)) |
1101 | goto close_connection; |
1102 | /* no need to write if no events */ |
1103 | if (!(c->poll_entry->revents & POLLOUT)) |
1104 | return 0; |
1105 | len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0); |
1106 | if (len < 0) { |
1107 | if (ff_neterrno() != AVERROR(EAGAIN) && |
1108 | ff_neterrno() != AVERROR(EINTR)) { |
1109 | goto close_connection; |
1110 | } |
1111 | break; |
1112 | } |
1113 | c->buffer_ptr += len; |
1114 | c->data_count += len; |
1115 | if (c->buffer_ptr >= c->buffer_end) { |
1116 | /* all the buffer was sent : wait for a new request */ |
1117 | av_freep(&c->pb_buffer); |
1118 | start_wait_request(c, 1); |
1119 | } |
1120 | break; |
1121 | case RTSPSTATE_SEND_PACKET: |
1122 | if (c->poll_entry->revents & (POLLERR | POLLHUP)) { |
1123 | av_freep(&c->packet_buffer); |
1124 | return -1; |
1125 | } |
1126 | /* no need to write if no events */ |
1127 | if (!(c->poll_entry->revents & POLLOUT)) |
1128 | return 0; |
1129 | len = send(c->fd, c->packet_buffer_ptr, |
1130 | c->packet_buffer_end - c->packet_buffer_ptr, 0); |
1131 | if (len < 0) { |
1132 | if (ff_neterrno() != AVERROR(EAGAIN) && |
1133 | ff_neterrno() != AVERROR(EINTR)) { |
1134 | /* error : close connection */ |
1135 | av_freep(&c->packet_buffer); |
1136 | return -1; |
1137 | } |
1138 | break; |
1139 | } |
1140 | c->packet_buffer_ptr += len; |
1141 | if (c->packet_buffer_ptr >= c->packet_buffer_end) { |
1142 | /* all the buffer was sent : wait for a new request */ |
1143 | av_freep(&c->packet_buffer); |
1144 | c->state = RTSPSTATE_WAIT_REQUEST; |
1145 | } |
1146 | break; |
1147 | case HTTPSTATE_READY: |
1148 | /* nothing to do */ |
1149 | break; |
1150 | default: |
1151 | return -1; |
1152 | } |
1153 | return 0; |
1154 | |
1155 | close_connection: |
1156 | av_freep(&c->pb_buffer); |
1157 | return -1; |
1158 | } |
1159 | |
1160 | static int extract_rates(char *rates, int ratelen, const char *request) |
1161 | { |
1162 | const char *p; |
1163 | |
1164 | for (p = request; *p && *p != '\r' && *p != '\n'; ) { |
1165 | if (av_strncasecmp(p, "Pragma:", 7) == 0) { |
1166 | const char *q = p + 7; |
1167 | |
1168 | while (*q && *q != '\n' && av_isspace(*q)) |
1169 | q++; |
1170 | |
1171 | if (av_strncasecmp(q, "stream-switch-entry=", 20) == 0) { |
1172 | int stream_no; |
1173 | int rate_no; |
1174 | |
1175 | q += 20; |
1176 | |
1177 | memset(rates, 0xff, ratelen); |
1178 | |
1179 | while (1) { |
1180 | while (*q && *q != '\n' && *q != ':') |
1181 | q++; |
1182 | |
1183 | if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2) |
1184 | break; |
1185 | |
1186 | stream_no--; |
1187 | if (stream_no < ratelen && stream_no >= 0) |
1188 | rates[stream_no] = rate_no; |
1189 | |
1190 | while (*q && *q != '\n' && !av_isspace(*q)) |
1191 | q++; |
1192 | } |
1193 | |
1194 | return 1; |
1195 | } |
1196 | } |
1197 | p = strchr(p, '\n'); |
1198 | if (!p) |
1199 | break; |
1200 | |
1201 | p++; |
1202 | } |
1203 | |
1204 | return 0; |
1205 | } |
1206 | |
1207 | static int find_stream_in_feed(FFServerStream *feed, AVCodecParameters *codec, |
1208 | int bit_rate) |
1209 | { |
1210 | int i; |
1211 | int best_bitrate = 100000000; |
1212 | int best = -1; |
1213 | |
1214 | for (i = 0; i < feed->nb_streams; i++) { |
1215 | AVCodecParameters *feed_codec = feed->streams[i]->codecpar; |
1216 | |
1217 | if (feed_codec->codec_id != codec->codec_id || |
1218 | feed_codec->sample_rate != codec->sample_rate || |
1219 | feed_codec->width != codec->width || |
1220 | feed_codec->height != codec->height) |
1221 | continue; |
1222 | |
1223 | /* Potential stream */ |
1224 | |
1225 | /* We want the fastest stream less than bit_rate, or the slowest |
1226 | * faster than bit_rate |
1227 | */ |
1228 | |
1229 | if (feed_codec->bit_rate <= bit_rate) { |
1230 | if (best_bitrate > bit_rate || |
1231 | feed_codec->bit_rate > best_bitrate) { |
1232 | best_bitrate = feed_codec->bit_rate; |
1233 | best = i; |
1234 | } |
1235 | continue; |
1236 | } |
1237 | if (feed_codec->bit_rate < best_bitrate) { |
1238 | best_bitrate = feed_codec->bit_rate; |
1239 | best = i; |
1240 | } |
1241 | } |
1242 | return best; |
1243 | } |
1244 | |
1245 | static int modify_current_stream(HTTPContext *c, char *rates) |
1246 | { |
1247 | int i; |
1248 | FFServerStream *req = c->stream; |
1249 | int action_required = 0; |
1250 | |
1251 | /* Not much we can do for a feed */ |
1252 | if (!req->feed) |
1253 | return 0; |
1254 | |
1255 | for (i = 0; i < req->nb_streams; i++) { |
1256 | AVCodecParameters *codec = req->streams[i]->codecpar; |
1257 | |
1258 | switch(rates[i]) { |
1259 | case 0: |
1260 | c->switch_feed_streams[i] = req->feed_streams[i]; |
1261 | break; |
1262 | case 1: |
1263 | c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2); |
1264 | break; |
1265 | case 2: |
1266 | /* Wants off or slow */ |
1267 | c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4); |
1268 | #ifdef WANTS_OFF |
1269 | /* This doesn't work well when it turns off the only stream! */ |
1270 | c->switch_feed_streams[i] = -2; |
1271 | c->feed_streams[i] = -2; |
1272 | #endif |
1273 | break; |
1274 | } |
1275 | |
1276 | if (c->switch_feed_streams[i] >= 0 && |
1277 | c->switch_feed_streams[i] != c->feed_streams[i]) { |
1278 | action_required = 1; |
1279 | } |
1280 | } |
1281 | |
1282 | return action_required; |
1283 | } |
1284 | |
1285 | static void get_word(char *buf, int buf_size, const char **pp) |
1286 | { |
1287 | const char *p; |
1288 | char *q; |
1289 | |
1290 | #define SPACE_CHARS " \t\r\n" |
1291 | |
1292 | p = *pp; |
1293 | p += strspn(p, SPACE_CHARS); |
1294 | q = buf; |
1295 | while (!av_isspace(*p) && *p != '\0') { |
1296 | if ((q - buf) < buf_size - 1) |
1297 | *q++ = *p; |
1298 | p++; |
1299 | } |
1300 | if (buf_size > 0) |
1301 | *q = '\0'; |
1302 | *pp = p; |
1303 | } |
1304 | |
1305 | static FFServerIPAddressACL* parse_dynamic_acl(FFServerStream *stream, |
1306 | HTTPContext *c) |
1307 | { |
1308 | FILE* f; |
1309 | char line[1024]; |
1310 | char cmd[1024]; |
1311 | FFServerIPAddressACL *acl = NULL; |
1312 | int line_num = 0; |
1313 | const char *p; |
1314 | |
1315 | f = fopen(stream->dynamic_acl, "r"); |
1316 | if (!f) { |
1317 | perror(stream->dynamic_acl); |
1318 | return NULL; |
1319 | } |
1320 | |
1321 | acl = av_mallocz(sizeof(FFServerIPAddressACL)); |
1322 | if (!acl) { |
1323 | fclose(f); |
1324 | return NULL; |
1325 | } |
1326 | |
1327 | /* Build ACL */ |
1328 | while (fgets(line, sizeof(line), f)) { |
1329 | line_num++; |
1330 | p = line; |
1331 | while (av_isspace(*p)) |
1332 | p++; |
1333 | if (*p == '\0' || *p == '#') |
1334 | continue; |
1335 | ffserver_get_arg(cmd, sizeof(cmd), &p); |
1336 | |
1337 | if (!av_strcasecmp(cmd, "ACL")) |
1338 | ffserver_parse_acl_row(NULL, NULL, acl, p, stream->dynamic_acl, |
1339 | line_num); |
1340 | } |
1341 | fclose(f); |
1342 | return acl; |
1343 | } |
1344 | |
1345 | |
1346 | static void free_acl_list(FFServerIPAddressACL *in_acl) |
1347 | { |
1348 | FFServerIPAddressACL *pacl, *pacl2; |
1349 | |
1350 | pacl = in_acl; |
1351 | while(pacl) { |
1352 | pacl2 = pacl; |
1353 | pacl = pacl->next; |
1354 | av_freep(pacl2); |
1355 | } |
1356 | } |
1357 | |
1358 | static int validate_acl_list(FFServerIPAddressACL *in_acl, HTTPContext *c) |
1359 | { |
1360 | enum FFServerIPAddressAction last_action = IP_DENY; |
1361 | FFServerIPAddressACL *acl; |
1362 | struct in_addr *src = &c->from_addr.sin_addr; |
1363 | unsigned long src_addr = src->s_addr; |
1364 | |
1365 | for (acl = in_acl; acl; acl = acl->next) { |
1366 | if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr) |
1367 | return (acl->action == IP_ALLOW) ? 1 : 0; |
1368 | last_action = acl->action; |
1369 | } |
1370 | |
1371 | /* Nothing matched, so return not the last action */ |
1372 | return (last_action == IP_DENY) ? 1 : 0; |
1373 | } |
1374 | |
1375 | static int validate_acl(FFServerStream *stream, HTTPContext *c) |
1376 | { |
1377 | int ret = 0; |
1378 | FFServerIPAddressACL *acl; |
1379 | |
1380 | /* if stream->acl is null validate_acl_list will return 1 */ |
1381 | ret = validate_acl_list(stream->acl, c); |
1382 | |
1383 | if (stream->dynamic_acl[0]) { |
1384 | acl = parse_dynamic_acl(stream, c); |
1385 | ret = validate_acl_list(acl, c); |
1386 | free_acl_list(acl); |
1387 | } |
1388 | |
1389 | return ret; |
1390 | } |
1391 | |
1392 | /** |
1393 | * compute the real filename of a file by matching it without its |
1394 | * extensions to all the stream's filenames |
1395 | */ |
1396 | static void compute_real_filename(char *filename, int max_size) |
1397 | { |
1398 | char file1[1024]; |
1399 | char file2[1024]; |
1400 | char *p; |
1401 | FFServerStream *stream; |
1402 | |
1403 | av_strlcpy(file1, filename, sizeof(file1)); |
1404 | p = strrchr(file1, '.'); |
1405 | if (p) |
1406 | *p = '\0'; |
1407 | for(stream = config.first_stream; stream; stream = stream->next) { |
1408 | av_strlcpy(file2, stream->filename, sizeof(file2)); |
1409 | p = strrchr(file2, '.'); |
1410 | if (p) |
1411 | *p = '\0'; |
1412 | if (!strcmp(file1, file2)) { |
1413 | av_strlcpy(filename, stream->filename, max_size); |
1414 | break; |
1415 | } |
1416 | } |
1417 | } |
1418 | |
1419 | enum RedirType { |
1420 | REDIR_NONE, |
1421 | REDIR_ASX, |
1422 | REDIR_RAM, |
1423 | REDIR_ASF, |
1424 | REDIR_RTSP, |
1425 | REDIR_SDP, |
1426 | }; |
1427 | |
1428 | /* parse HTTP request and prepare header */ |
1429 | static int http_parse_request(HTTPContext *c) |
1430 | { |
1431 | const char *p; |
1432 | char *p1; |
1433 | enum RedirType redir_type; |
1434 | char cmd[32]; |
1435 | char info[1024], filename[1024]; |
1436 | char url[1024], *q; |
1437 | char protocol[32]; |
1438 | char msg[1024]; |
1439 | char *encoded_msg = NULL; |
1440 | const char *mime_type; |
1441 | FFServerStream *stream; |
1442 | int i; |
1443 | char ratebuf[32]; |
1444 | const char *useragent = 0; |
1445 | |
1446 | p = c->buffer; |
1447 | get_word(cmd, sizeof(cmd), &p); |
1448 | av_strlcpy(c->method, cmd, sizeof(c->method)); |
1449 | |
1450 | if (!strcmp(cmd, "GET")) |
1451 | c->post = 0; |
1452 | else if (!strcmp(cmd, "POST")) |
1453 | c->post = 1; |
1454 | else |
1455 | return -1; |
1456 | |
1457 | get_word(url, sizeof(url), &p); |
1458 | av_strlcpy(c->url, url, sizeof(c->url)); |
1459 | |
1460 | get_word(protocol, sizeof(protocol), (const char **)&p); |
1461 | if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1")) |
1462 | return -1; |
1463 | |
1464 | av_strlcpy(c->protocol, protocol, sizeof(c->protocol)); |
1465 | |
1466 | if (config.debug) |
1467 | http_log("%s - - New connection: %s %s\n", |
1468 | inet_ntoa(c->from_addr.sin_addr), cmd, url); |
1469 | |
1470 | /* find the filename and the optional info string in the request */ |
1471 | p1 = strchr(url, '?'); |
1472 | if (p1) { |
1473 | av_strlcpy(info, p1, sizeof(info)); |
1474 | *p1 = '\0'; |
1475 | } else |
1476 | info[0] = '\0'; |
1477 | |
1478 | av_strlcpy(filename, url + ((*url == '/') ? 1 : 0), sizeof(filename)-1); |
1479 | |
1480 | for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) { |
1481 | if (av_strncasecmp(p, "User-Agent:", 11) == 0) { |
1482 | useragent = p + 11; |
1483 | if (*useragent && *useragent != '\n' && av_isspace(*useragent)) |
1484 | useragent++; |
1485 | break; |
1486 | } |
1487 | p = strchr(p, '\n'); |
1488 | if (!p) |
1489 | break; |
1490 | |
1491 | p++; |
1492 | } |
1493 | |
1494 | redir_type = REDIR_NONE; |
1495 | if (av_match_ext(filename, "asx")) { |
1496 | redir_type = REDIR_ASX; |
1497 | filename[strlen(filename)-1] = 'f'; |
1498 | } else if (av_match_ext(filename, "asf") && |
1499 | (!useragent || av_strncasecmp(useragent, "NSPlayer", 8))) { |
1500 | /* if this isn't WMP or lookalike, return the redirector file */ |
1501 | redir_type = REDIR_ASF; |
1502 | } else if (av_match_ext(filename, "rpm,ram")) { |
1503 | redir_type = REDIR_RAM; |
1504 | strcpy(filename + strlen(filename)-2, "m"); |
1505 | } else if (av_match_ext(filename, "rtsp")) { |
1506 | redir_type = REDIR_RTSP; |
1507 | compute_real_filename(filename, sizeof(filename) - 1); |
1508 | } else if (av_match_ext(filename, "sdp")) { |
1509 | redir_type = REDIR_SDP; |
1510 | compute_real_filename(filename, sizeof(filename) - 1); |
1511 | } |
1512 | |
1513 | /* "redirect" request to index.html */ |
1514 | if (!strlen(filename)) |
1515 | av_strlcpy(filename, "index.html", sizeof(filename) - 1); |
1516 | |
1517 | stream = config.first_stream; |
1518 | while (stream) { |
1519 | if (!strcmp(stream->filename, filename) && validate_acl(stream, c)) |
1520 | break; |
1521 | stream = stream->next; |
1522 | } |
1523 | if (!stream) { |
1524 | snprintf(msg, sizeof(msg), "File '%s' not found", url); |
1525 | http_log("File '%s' not found\n", url); |
1526 | goto send_error; |
1527 | } |
1528 | |
1529 | c->stream = stream; |
1530 | memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams)); |
1531 | memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams)); |
1532 | |
1533 | if (stream->stream_type == STREAM_TYPE_REDIRECT) { |
1534 | c->http_error = 301; |
1535 | q = c->buffer; |
1536 | snprintf(q, c->buffer_size, |
1537 | "HTTP/1.0 301 Moved\r\n" |
1538 | "Location: %s\r\n" |
1539 | "Content-type: text/html\r\n" |
1540 | "\r\n" |
1541 | "<!DOCTYPE html>\n" |
1542 | "<html><head><title>Moved</title></head><body>\r\n" |
1543 | "You should be <a href=\"%s\">redirected</a>.\r\n" |
1544 | "</body></html>\r\n", |
1545 | stream->feed_filename, stream->feed_filename); |
1546 | q += strlen(q); |
1547 | /* prepare output buffer */ |
1548 | c->buffer_ptr = c->buffer; |
1549 | c->buffer_end = q; |
1550 | c->state = HTTPSTATE_SEND_HEADER; |
1551 | return 0; |
1552 | } |
1553 | |
1554 | /* If this is WMP, get the rate information */ |
1555 | if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) { |
1556 | if (modify_current_stream(c, ratebuf)) { |
1557 | for (i = 0; i < FF_ARRAY_ELEMS(c->feed_streams); i++) { |
1558 | if (c->switch_feed_streams[i] >= 0) |
1559 | c->switch_feed_streams[i] = -1; |
1560 | } |
1561 | } |
1562 | } |
1563 | |
1564 | if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE) |
1565 | current_bandwidth += stream->bandwidth; |
1566 | |
1567 | /* If already streaming this feed, do not let another feeder start */ |
1568 | if (stream->feed_opened) { |
1569 | snprintf(msg, sizeof(msg), "This feed is already being received."); |
1570 | http_log("Feed '%s' already being received\n", stream->feed_filename); |
1571 | goto send_error; |
1572 | } |
1573 | |
1574 | if (c->post == 0 && config.max_bandwidth < current_bandwidth) { |
1575 | c->http_error = 503; |
1576 | q = c->buffer; |
1577 | snprintf(q, c->buffer_size, |
1578 | "HTTP/1.0 503 Server too busy\r\n" |
1579 | "Content-type: text/html\r\n" |
1580 | "\r\n" |
1581 | "<!DOCTYPE html>\n" |
1582 | "<html><head><title>Too busy</title></head><body>\r\n" |
1583 | "<p>The server is too busy to serve your request at " |
1584 | "this time.</p>\r\n" |
1585 | "<p>The bandwidth being served (including your stream) " |
1586 | "is %"PRIu64"kbit/s, and this exceeds the limit of " |
1587 | "%"PRIu64"kbit/s.</p>\r\n" |
1588 | "</body></html>\r\n", |
1589 | current_bandwidth, config.max_bandwidth); |
1590 | q += strlen(q); |
1591 | /* prepare output buffer */ |
1592 | c->buffer_ptr = c->buffer; |
1593 | c->buffer_end = q; |
1594 | c->state = HTTPSTATE_SEND_HEADER; |
1595 | return 0; |
1596 | } |
1597 | |
1598 | if (redir_type != REDIR_NONE) { |
1599 | const char *hostinfo = 0; |
1600 | |
1601 | for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) { |
1602 | if (av_strncasecmp(p, "Host:", 5) == 0) { |
1603 | hostinfo = p + 5; |
1604 | break; |
1605 | } |
1606 | p = strchr(p, '\n'); |
1607 | if (!p) |
1608 | break; |
1609 | |
1610 | p++; |
1611 | } |
1612 | |
1613 | if (hostinfo) { |
1614 | char *eoh; |
1615 | char hostbuf[260]; |
1616 | |
1617 | while (av_isspace(*hostinfo)) |
1618 | hostinfo++; |
1619 | |
1620 | eoh = strchr(hostinfo, '\n'); |
1621 | if (eoh) { |
1622 | if (eoh[-1] == '\r') |
1623 | eoh--; |
1624 | |
1625 | if (eoh - hostinfo < sizeof(hostbuf) - 1) { |
1626 | memcpy(hostbuf, hostinfo, eoh - hostinfo); |
1627 | hostbuf[eoh - hostinfo] = 0; |
1628 | |
1629 | c->http_error = 200; |
1630 | q = c->buffer; |
1631 | switch(redir_type) { |
1632 | case REDIR_ASX: |
1633 | snprintf(q, c->buffer_size, |
1634 | "HTTP/1.0 200 ASX Follows\r\n" |
1635 | "Content-type: video/x-ms-asf\r\n" |
1636 | "\r\n" |
1637 | "<ASX Version=\"3\">\r\n" |
1638 | //"<!-- Autogenerated by ffserver -->\r\n" |
1639 | "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n" |
1640 | "</ASX>\r\n", hostbuf, filename, info); |
1641 | q += strlen(q); |
1642 | break; |
1643 | case REDIR_RAM: |
1644 | snprintf(q, c->buffer_size, |
1645 | "HTTP/1.0 200 RAM Follows\r\n" |
1646 | "Content-type: audio/x-pn-realaudio\r\n" |
1647 | "\r\n" |
1648 | "# Autogenerated by ffserver\r\n" |
1649 | "http://%s/%s%s\r\n", hostbuf, filename, info); |
1650 | q += strlen(q); |
1651 | break; |
1652 | case REDIR_ASF: |
1653 | snprintf(q, c->buffer_size, |
1654 | "HTTP/1.0 200 ASF Redirect follows\r\n" |
1655 | "Content-type: video/x-ms-asf\r\n" |
1656 | "\r\n" |
1657 | "[Reference]\r\n" |
1658 | "Ref1=http://%s/%s%s\r\n", hostbuf, filename, info); |
1659 | q += strlen(q); |
1660 | break; |
1661 | case REDIR_RTSP: |
1662 | { |
1663 | char hostname[256], *p; |
1664 | /* extract only hostname */ |
1665 | av_strlcpy(hostname, hostbuf, sizeof(hostname)); |
1666 | p = strrchr(hostname, ':'); |
1667 | if (p) |
1668 | *p = '\0'; |
1669 | snprintf(q, c->buffer_size, |
1670 | "HTTP/1.0 200 RTSP Redirect follows\r\n" |
1671 | /* XXX: incorrect MIME type ? */ |
1672 | "Content-type: application/x-rtsp\r\n" |
1673 | "\r\n" |
1674 | "rtsp://%s:%d/%s\r\n", hostname, ntohs(config.rtsp_addr.sin_port), filename); |
1675 | q += strlen(q); |
1676 | } |
1677 | break; |
1678 | case REDIR_SDP: |
1679 | { |
1680 | uint8_t *sdp_data; |
1681 | int sdp_data_size; |
1682 | socklen_t len; |
1683 | struct sockaddr_in my_addr; |
1684 | |
1685 | snprintf(q, c->buffer_size, |
1686 | "HTTP/1.0 200 OK\r\n" |
1687 | "Content-type: application/sdp\r\n" |
1688 | "\r\n"); |
1689 | q += strlen(q); |
1690 | |
1691 | len = sizeof(my_addr); |
1692 | |
1693 | /* XXX: Should probably fail? */ |
1694 | if (getsockname(c->fd, (struct sockaddr *)&my_addr, &len)) |
1695 | http_log("getsockname() failed\n"); |
1696 | |
1697 | /* XXX: should use a dynamic buffer */ |
1698 | sdp_data_size = prepare_sdp_description(stream, |
1699 | &sdp_data, |
1700 | my_addr.sin_addr); |
1701 | if (sdp_data_size > 0) { |
1702 | memcpy(q, sdp_data, sdp_data_size); |
1703 | q += sdp_data_size; |
1704 | *q = '\0'; |
1705 | av_freep(&sdp_data); |
1706 | } |
1707 | } |
1708 | break; |
1709 | default: |
1710 | abort(); |
1711 | break; |
1712 | } |
1713 | |
1714 | /* prepare output buffer */ |
1715 | c->buffer_ptr = c->buffer; |
1716 | c->buffer_end = q; |
1717 | c->state = HTTPSTATE_SEND_HEADER; |
1718 | return 0; |
1719 | } |
1720 | } |
1721 | } |
1722 | |
1723 | snprintf(msg, sizeof(msg), "ASX/RAM file not handled"); |
1724 | goto send_error; |
1725 | } |
1726 | |
1727 | stream->conns_served++; |
1728 | |
1729 | /* XXX: add there authenticate and IP match */ |
1730 | |
1731 | if (c->post) { |
1732 | /* if post, it means a feed is being sent */ |
1733 | if (!stream->is_feed) { |
1734 | /* However it might be a status report from WMP! Let us log the |
1735 | * data as it might come handy one day. */ |
1736 | const char *logline = 0; |
1737 | int client_id = 0; |
1738 | |
1739 | for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) { |
1740 | if (av_strncasecmp(p, "Pragma: log-line=", 17) == 0) { |
1741 | logline = p; |
1742 | break; |
1743 | } |
1744 | if (av_strncasecmp(p, "Pragma: client-id=", 18) == 0) |
1745 | client_id = strtol(p + 18, 0, 10); |
1746 | p = strchr(p, '\n'); |
1747 | if (!p) |
1748 | break; |
1749 | |
1750 | p++; |
1751 | } |
1752 | |
1753 | if (logline) { |
1754 | char *eol = strchr(logline, '\n'); |
1755 | |
1756 | logline += 17; |
1757 | |
1758 | if (eol) { |
1759 | if (eol[-1] == '\r') |
1760 | eol--; |
1761 | http_log("%.*s\n", (int) (eol - logline), logline); |
1762 | c->suppress_log = 1; |
1763 | } |
1764 | } |
1765 | |
1766 | #ifdef DEBUG |
1767 | http_log("\nGot request:\n%s\n", c->buffer); |
1768 | #endif |
1769 | |
1770 | if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) { |
1771 | HTTPContext *wmpc; |
1772 | |
1773 | /* Now we have to find the client_id */ |
1774 | for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) { |
1775 | if (wmpc->wmp_client_id == client_id) |
1776 | break; |
1777 | } |
1778 | |
1779 | if (wmpc && modify_current_stream(wmpc, ratebuf)) |
1780 | wmpc->switch_pending = 1; |
1781 | } |
1782 | |
1783 | snprintf(msg, sizeof(msg), "POST command not handled"); |
1784 | c->stream = 0; |
1785 | goto send_error; |
1786 | } |
1787 | if (http_start_receive_data(c) < 0) { |
1788 | snprintf(msg, sizeof(msg), "could not open feed"); |
1789 | goto send_error; |
1790 | } |
1791 | c->http_error = 0; |
1792 | c->state = HTTPSTATE_RECEIVE_DATA; |
1793 | return 0; |
1794 | } |
1795 | |
1796 | #ifdef DEBUG |
1797 | if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0) |
1798 | http_log("\nGot request:\n%s\n", c->buffer); |
1799 | #endif |
1800 | |
1801 | if (c->stream->stream_type == STREAM_TYPE_STATUS) |
1802 | goto send_status; |
1803 | |
1804 | /* open input stream */ |
1805 | if (open_input_stream(c, info) < 0) { |
1806 | snprintf(msg, sizeof(msg), "Input stream corresponding to '%s' not found", url); |
1807 | goto send_error; |
1808 | } |
1809 | |
1810 | /* prepare HTTP header */ |
1811 | c->buffer[0] = 0; |
1812 | av_strlcatf(c->buffer, c->buffer_size, "HTTP/1.0 200 OK\r\n"); |
1813 | mime_type = c->stream->fmt->mime_type; |
1814 | if (!mime_type) |
1815 | mime_type = "application/x-octet-stream"; |
1816 | av_strlcatf(c->buffer, c->buffer_size, "Pragma: no-cache\r\n"); |
1817 | |
1818 | /* for asf, we need extra headers */ |
1819 | if (!strcmp(c->stream->fmt->name,"asf_stream")) { |
1820 | /* Need to allocate a client id */ |
1821 | |
1822 | c->wmp_client_id = av_lfg_get(&random_state); |
1823 | |
1824 | av_strlcatf(c->buffer, c->buffer_size, "Server: Cougar 4.1.0.3923\r\nCache-Control: no-cache\r\nPragma: client-id=%d\r\nPragma: features=\"broadcast\"\r\n", c->wmp_client_id); |
1825 | } |
1826 | av_strlcatf(c->buffer, c->buffer_size, "Content-Type: %s\r\n", mime_type); |
1827 | av_strlcatf(c->buffer, c->buffer_size, "\r\n"); |
1828 | q = c->buffer + strlen(c->buffer); |
1829 | |
1830 | /* prepare output buffer */ |
1831 | c->http_error = 0; |
1832 | c->buffer_ptr = c->buffer; |
1833 | c->buffer_end = q; |
1834 | c->state = HTTPSTATE_SEND_HEADER; |
1835 | return 0; |
1836 | send_error: |
1837 | c->http_error = 404; |
1838 | q = c->buffer; |
1839 | if (!htmlencode(msg, &encoded_msg)) { |
1840 | http_log("Could not encode filename '%s' as HTML\n", msg); |
1841 | } |
1842 | snprintf(q, c->buffer_size, |
1843 | "HTTP/1.0 404 Not Found\r\n" |
1844 | "Content-type: text/html\r\n" |
1845 | "\r\n" |
1846 | "<!DOCTYPE html>\n" |
1847 | "<html>\n" |
1848 | "<head>\n" |
1849 | "<meta charset=\"UTF-8\">\n" |
1850 | "<title>404 Not Found</title>\n" |
1851 | "</head>\n" |
1852 | "<body>%s</body>\n" |
1853 | "</html>\n", encoded_msg? encoded_msg : "File not found"); |
1854 | q += strlen(q); |
1855 | /* prepare output buffer */ |
1856 | c->buffer_ptr = c->buffer; |
1857 | c->buffer_end = q; |
1858 | c->state = HTTPSTATE_SEND_HEADER; |
1859 | av_freep(&encoded_msg); |
1860 | return 0; |
1861 | send_status: |
1862 | compute_status(c); |
1863 | /* horrible: we use this value to avoid |
1864 | * going to the send data state */ |
1865 | c->http_error = 200; |
1866 | c->state = HTTPSTATE_SEND_HEADER; |
1867 | return 0; |
1868 | } |
1869 | |
1870 | static void fmt_bytecount(AVIOContext *pb, int64_t count) |
1871 | { |
1872 | static const char suffix[] = " kMGTP"; |
1873 | const char *s; |
1874 | |
1875 | for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++); |
1876 | |
1877 | avio_printf(pb, "%"PRId64"%c", count, *s); |
1878 | } |
1879 | |
1880 | static inline void print_stream_params(AVIOContext *pb, FFServerStream *stream) |
1881 | { |
1882 | int i, stream_no; |
1883 | const char *type = "unknown"; |
1884 | char parameters[64]; |
1885 | LayeredAVStream *st; |
1886 | AVCodec *codec; |
1887 | |
1888 | stream_no = stream->nb_streams; |
1889 | |
1890 | avio_printf(pb, "<table><tr><th>Stream<th>" |
1891 | "type<th>kbit/s<th>codec<th>" |
1892 | "Parameters\n"); |
1893 | |
1894 | for (i = 0; i < stream_no; i++) { |
1895 | st = stream->streams[i]; |
1896 | codec = avcodec_find_encoder(st->codecpar->codec_id); |
1897 | |
1898 | parameters[0] = 0; |
1899 | |
1900 | switch(st->codecpar->codec_type) { |
1901 | case AVMEDIA_TYPE_AUDIO: |
1902 | type = "audio"; |
1903 | snprintf(parameters, sizeof(parameters), "%d channel(s), %d Hz", |
1904 | st->codecpar->channels, st->codecpar->sample_rate); |
1905 | break; |
1906 | case AVMEDIA_TYPE_VIDEO: |
1907 | type = "video"; |
1908 | snprintf(parameters, sizeof(parameters), |
1909 | "%dx%d, q=%d-%d, fps=%d", st->codecpar->width, |
1910 | st->codecpar->height, st->codec->qmin, st->codec->qmax, |
1911 | st->time_base.den / st->time_base.num); |
1912 | break; |
1913 | default: |
1914 | abort(); |
1915 | } |
1916 | |
1917 | avio_printf(pb, "<tr><td>%d<td>%s<td>%"PRId64 |
1918 | "<td>%s<td>%s\n", |
1919 | i, type, (int64_t)st->codecpar->bit_rate/1000, |
1920 | codec ? codec->name : "", parameters); |
1921 | } |
1922 | |
1923 | avio_printf(pb, "</table>\n"); |
1924 | } |
1925 | |
1926 | static void clean_html(char *clean, int clean_len, char *dirty) |
1927 | { |
1928 | int i, o; |
1929 | |
1930 | for (o = i = 0; o+10 < clean_len && dirty[i];) { |
1931 | int len = strspn(dirty+i, "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$-_.+!*(),?/ :;%"); |
1932 | if (len) { |
1933 | if (o + len >= clean_len) |
1934 | break; |
1935 | memcpy(clean + o, dirty + i, len); |
1936 | i += len; |
1937 | o += len; |
1938 | } else { |
1939 | int c = dirty[i++]; |
1940 | switch (c) { |
1941 | case '&': av_strlcat(clean+o, "&" , clean_len - o); break; |
1942 | case '<': av_strlcat(clean+o, "<" , clean_len - o); break; |
1943 | case '>': av_strlcat(clean+o, ">" , clean_len - o); break; |
1944 | case '\'': av_strlcat(clean+o, "'" , clean_len - o); break; |
1945 | case '\"': av_strlcat(clean+o, """ , clean_len - o); break; |
1946 | default: av_strlcat(clean+o, "☹", clean_len - o); break; |
1947 | } |
1948 | o += strlen(clean+o); |
1949 | } |
1950 | } |
1951 | clean[o] = 0; |
1952 | } |
1953 | |
1954 | static void compute_status(HTTPContext *c) |
1955 | { |
1956 | HTTPContext *c1; |
1957 | FFServerStream *stream; |
1958 | char *p; |
1959 | time_t ti; |
1960 | int i, len; |
1961 | AVIOContext *pb; |
1962 | |
1963 | if (avio_open_dyn_buf(&pb) < 0) { |
1964 | /* XXX: return an error ? */ |
1965 | c->buffer_ptr = c->buffer; |
1966 | c->buffer_end = c->buffer; |
1967 | return; |
1968 | } |
1969 | |
1970 | avio_printf(pb, "HTTP/1.0 200 OK\r\n"); |
1971 | avio_printf(pb, "Content-type: text/html\r\n"); |
1972 | avio_printf(pb, "Pragma: no-cache\r\n"); |
1973 | avio_printf(pb, "\r\n"); |
1974 | |
1975 | avio_printf(pb, "<!DOCTYPE html>\n"); |
1976 | avio_printf(pb, "<html><head><title>%s Status</title>\n", program_name); |
1977 | if (c->stream->feed_filename[0]) |
1978 | avio_printf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", |
1979 | c->stream->feed_filename); |
1980 | avio_printf(pb, "</head>\n<body>"); |
1981 | avio_printf(pb, "<h1>%s Status</h1>\n", program_name); |
1982 | /* format status */ |
1983 | avio_printf(pb, "<h2>Available Streams</h2>\n"); |
1984 | avio_printf(pb, "<table>\n"); |
1985 | avio_printf(pb, "<tr><th>Path<th>Served<br>Conns<th><br>bytes<th>Format<th>Bit rate<br>kbit/s<th>Video<br>kbit/s<th><br>Codec<th>Audio<br>kbit/s<th><br>Codec<th>Feed\n"); |
1986 | stream = config.first_stream; |
1987 | while (stream) { |
1988 | char sfilename[1024]; |
1989 | char *eosf; |
1990 | |
1991 | if (stream->feed == stream) { |
1992 | stream = stream->next; |
1993 | continue; |
1994 | } |
1995 | |
1996 | av_strlcpy(sfilename, stream->filename, sizeof(sfilename) - 10); |
1997 | eosf = sfilename + strlen(sfilename); |
1998 | if (eosf - sfilename >= 4) { |
1999 | if (strcmp(eosf - 4, ".asf") == 0) |
2000 | strcpy(eosf - 4, ".asx"); |
2001 | else if (strcmp(eosf - 3, ".rm") == 0) |
2002 | strcpy(eosf - 3, ".ram"); |
2003 | else if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) { |
2004 | /* generate a sample RTSP director if |
2005 | * unicast. Generate an SDP redirector if |
2006 | * multicast */ |
2007 | eosf = strrchr(sfilename, '.'); |
2008 | if (!eosf) |
2009 | eosf = sfilename + strlen(sfilename); |
2010 | if (stream->is_multicast) |
2011 | strcpy(eosf, ".sdp"); |
2012 | else |
2013 | strcpy(eosf, ".rtsp"); |
2014 | } |
2015 | } |
2016 | |
2017 | avio_printf(pb, "<tr><td><a href=\"/%s\">%s</a> ", |
2018 | sfilename, stream->filename); |
2019 | avio_printf(pb, "<td> %d <td> ", |
2020 | stream->conns_served); |
2021 | // TODO: Investigate if we can make http bitexact so it always produces the same count of bytes |
2022 | if (!config.bitexact) |
2023 | fmt_bytecount(pb, stream->bytes_served); |
2024 | |
2025 | switch(stream->stream_type) { |
2026 | case STREAM_TYPE_LIVE: { |
2027 | int audio_bit_rate = 0; |
2028 | int video_bit_rate = 0; |
2029 | const char *audio_codec_name = ""; |
2030 | const char *video_codec_name = ""; |
2031 | const char *audio_codec_name_extra = ""; |
2032 | const char *video_codec_name_extra = ""; |
2033 | |
2034 | for(i=0;i<stream->nb_streams;i++) { |
2035 | LayeredAVStream *st = stream->streams[i]; |
2036 | AVCodec *codec = avcodec_find_encoder(st->codecpar->codec_id); |
2037 | |
2038 | switch(st->codecpar->codec_type) { |
2039 | case AVMEDIA_TYPE_AUDIO: |
2040 | audio_bit_rate += st->codecpar->bit_rate; |
2041 | if (codec) { |
2042 | if (*audio_codec_name) |
2043 | audio_codec_name_extra = "..."; |
2044 | audio_codec_name = codec->name; |
2045 | } |
2046 | break; |
2047 | case AVMEDIA_TYPE_VIDEO: |
2048 | video_bit_rate += st->codecpar->bit_rate; |
2049 | if (codec) { |
2050 | if (*video_codec_name) |
2051 | video_codec_name_extra = "..."; |
2052 | video_codec_name = codec->name; |
2053 | } |
2054 | break; |
2055 | case AVMEDIA_TYPE_DATA: |
2056 | video_bit_rate += st->codecpar->bit_rate; |
2057 | break; |
2058 | default: |
2059 | abort(); |
2060 | } |
2061 | } |
2062 | |
2063 | avio_printf(pb, "<td> %s <td> %d <td> %d <td> %s %s <td> " |
2064 | "%d <td> %s %s", |
2065 | stream->fmt->name, stream->bandwidth, |
2066 | video_bit_rate / 1000, video_codec_name, |
2067 | video_codec_name_extra, audio_bit_rate / 1000, |
2068 | audio_codec_name, audio_codec_name_extra); |
2069 | |
2070 | if (stream->feed) |
2071 | avio_printf(pb, "<td>%s", stream->feed->filename); |
2072 | else |
2073 | avio_printf(pb, "<td>%s", stream->feed_filename); |
2074 | avio_printf(pb, "\n"); |
2075 | } |
2076 | break; |
2077 | default: |
2078 | avio_printf(pb, "<td> - <td> - " |
2079 | "<td> - <td><td> - <td>\n"); |
2080 | break; |
2081 | } |
2082 | stream = stream->next; |
2083 | } |
2084 | avio_printf(pb, "</table>\n"); |
2085 | |
2086 | stream = config.first_stream; |
2087 | while (stream) { |
2088 | |
2089 | if (stream->feed != stream) { |
2090 | stream = stream->next; |
2091 | continue; |
2092 | } |
2093 | |
2094 | avio_printf(pb, "<h2>Feed %s</h2>", stream->filename); |
2095 | if (stream->pid) { |
2096 | avio_printf(pb, "Running as pid %"PRId64".\n", (int64_t) stream->pid); |
2097 | |
2098 | #if defined(linux) |
2099 | { |
2100 | FILE *pid_stat; |
2101 | char ps_cmd[64]; |
2102 | |
2103 | /* This is somewhat linux specific I guess */ |
2104 | snprintf(ps_cmd, sizeof(ps_cmd), |
2105 | "ps -o \"%%cpu,cputime\" --no-headers %"PRId64"", |
2106 | (int64_t) stream->pid); |
2107 | |
2108 | pid_stat = popen(ps_cmd, "r"); |
2109 | if (pid_stat) { |
2110 | char cpuperc[10]; |
2111 | char cpuused[64]; |
2112 | |
2113 | if (fscanf(pid_stat, "%9s %63s", cpuperc, cpuused) == 2) { |
2114 | avio_printf(pb, "Currently using %s%% of the cpu. " |
2115 | "Total time used %s.\n", |
2116 | cpuperc, cpuused); |
2117 | } |
2118 | fclose(pid_stat); |
2119 | } |
2120 | } |
2121 | #endif |
2122 | |
2123 | avio_printf(pb, "<p>"); |
2124 | } |
2125 | |
2126 | print_stream_params(pb, stream); |
2127 | stream = stream->next; |
2128 | } |
2129 | |
2130 | /* connection status */ |
2131 | avio_printf(pb, "<h2>Connection Status</h2>\n"); |
2132 | |
2133 | avio_printf(pb, "Number of connections: %d / %d<br>\n", |
2134 | nb_connections, config.nb_max_connections); |
2135 | |
2136 | avio_printf(pb, "Bandwidth in use: %"PRIu64"k / %"PRIu64"k<br>\n", |
2137 | current_bandwidth, config.max_bandwidth); |
2138 | |
2139 | avio_printf(pb, "<table>\n"); |
2140 | avio_printf(pb, "<tr><th>#<th>File<th>IP<th>URL<th>Proto<th>State<th>Target " |
2141 | "bit/s<th>Actual bit/s<th>Bytes transferred\n"); |
2142 | c1 = first_http_ctx; |
2143 | i = 0; |
2144 | while (c1) { |
2145 | int bitrate; |
2146 | int j; |
2147 | |
2148 | bitrate = 0; |
2149 | if (c1->stream) { |
2150 | for (j = 0; j < c1->stream->nb_streams; j++) { |
2151 | if (!c1->stream->feed) |
2152 | bitrate += c1->stream->streams[j]->codecpar->bit_rate; |
2153 | else if (c1->feed_streams[j] >= 0) |
2154 | bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codecpar->bit_rate; |
2155 | } |
2156 | } |
2157 | |
2158 | i++; |
2159 | p = inet_ntoa(c1->from_addr.sin_addr); |
2160 | clean_html(c1->clean_url, sizeof(c1->clean_url), c1->url); |
2161 | avio_printf(pb, "<tr><td><b>%d</b><td>%s%s<td>%s<td>%s<td>%s<td>%s" |
2162 | "<td>", |
2163 | i, c1->stream ? c1->stream->filename : "", |
2164 | c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "", |
2165 | p, |
2166 | c1->clean_url, |
2167 | c1->protocol, http_state[c1->state]); |
2168 | fmt_bytecount(pb, bitrate); |
2169 | avio_printf(pb, "<td>"); |
2170 | fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8); |
2171 | avio_printf(pb, "<td>"); |
2172 | fmt_bytecount(pb, c1->data_count); |
2173 | avio_printf(pb, "\n"); |
2174 | c1 = c1->next; |
2175 | } |
2176 | avio_printf(pb, "</table>\n"); |
2177 | |
2178 | if (!config.bitexact) { |
2179 | /* date */ |
2180 | ti = time(NULL); |
2181 | p = ctime(&ti); |
2182 | avio_printf(pb, "<hr>Generated at %s", p); |
2183 | } |
2184 | avio_printf(pb, "</body>\n</html>\n"); |
2185 | |
2186 | len = avio_close_dyn_buf(pb, &c->pb_buffer); |
2187 | c->buffer_ptr = c->pb_buffer; |
2188 | c->buffer_end = c->pb_buffer + len; |
2189 | } |
2190 | |
2191 | static int open_input_stream(HTTPContext *c, const char *info) |
2192 | { |
2193 | char buf[128]; |
2194 | char input_filename[1024]; |
2195 | AVFormatContext *s = NULL; |
2196 | int buf_size, i, ret; |
2197 | int64_t stream_pos; |
2198 | |
2199 | /* find file name */ |
2200 | if (c->stream->feed) { |
2201 | strcpy(input_filename, c->stream->feed->feed_filename); |
2202 | buf_size = FFM_PACKET_SIZE; |
2203 | /* compute position (absolute time) */ |
2204 | if (av_find_info_tag(buf, sizeof(buf), "date", info)) { |
2205 | if ((ret = av_parse_time(&stream_pos, buf, 0)) < 0) { |
2206 | http_log("Invalid date specification '%s' for stream\n", buf); |
2207 | return ret; |
2208 | } |
2209 | } else if (av_find_info_tag(buf, sizeof(buf), "buffer", info)) { |
2210 | int prebuffer = strtol(buf, 0, 10); |
2211 | stream_pos = av_gettime() - prebuffer * (int64_t)1000000; |
2212 | } else |
2213 | stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000; |
2214 | } else { |
2215 | strcpy(input_filename, c->stream->feed_filename); |
2216 | buf_size = 0; |
2217 | /* compute position (relative time) */ |
2218 | if (av_find_info_tag(buf, sizeof(buf), "date", info)) { |
2219 | if ((ret = av_parse_time(&stream_pos, buf, 1)) < 0) { |
2220 | http_log("Invalid date specification '%s' for stream\n", buf); |
2221 | return ret; |
2222 | } |
2223 | } else |
2224 | stream_pos = 0; |
2225 | } |
2226 | if (!input_filename[0]) { |
2227 | http_log("No filename was specified for stream\n"); |
2228 | return AVERROR(EINVAL); |
2229 | } |
2230 | |
2231 | /* open stream */ |
2232 | ret = avformat_open_input(&s, input_filename, c->stream->ifmt, |
2233 | &c->stream->in_opts); |
2234 | if (ret < 0) { |
2235 | http_log("Could not open input '%s': %s\n", |
2236 | input_filename, av_err2str(ret)); |
2237 | return ret; |
2238 | } |
2239 | |
2240 | /* set buffer size */ |
2241 | if (buf_size > 0) { |
2242 | ret = ffio_set_buf_size(s->pb, buf_size); |
2243 | if (ret < 0) { |
2244 | http_log("Failed to set buffer size\n"); |
2245 | return ret; |
2246 | } |
2247 | } |
2248 | |
2249 | s->flags |= AVFMT_FLAG_GENPTS; |
2250 | c->fmt_in = s; |
2251 | if (strcmp(s->iformat->name, "ffm") && |
2252 | (ret = avformat_find_stream_info(c->fmt_in, NULL)) < 0) { |
2253 | http_log("Could not find stream info for input '%s'\n", input_filename); |
2254 | avformat_close_input(&s); |
2255 | return ret; |
2256 | } |
2257 | |
2258 | /* choose stream as clock source (we favor the video stream if |
2259 | * present) for packet sending */ |
2260 | c->pts_stream_index = 0; |
2261 | for(i=0;i<c->stream->nb_streams;i++) { |
2262 | if (c->pts_stream_index == 0 && |
2263 | c->stream->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { |
2264 | c->pts_stream_index = i; |
2265 | } |
2266 | } |
2267 | |
2268 | if (c->fmt_in->iformat->read_seek) |
2269 | av_seek_frame(c->fmt_in, -1, stream_pos, 0); |
2270 | /* set the start time (needed for maxtime and RTP packet timing) */ |
2271 | c->start_time = cur_time; |
2272 | c->first_pts = AV_NOPTS_VALUE; |
2273 | return 0; |
2274 | } |
2275 | |
2276 | /* return the server clock (in us) */ |
2277 | static int64_t get_server_clock(HTTPContext *c) |
2278 | { |
2279 | /* compute current pts value from system time */ |
2280 | return (cur_time - c->start_time) * 1000; |
2281 | } |
2282 | |
2283 | /* return the estimated time (in us) at which the current packet must be sent */ |
2284 | static int64_t get_packet_send_clock(HTTPContext *c) |
2285 | { |
2286 | int bytes_left, bytes_sent, frame_bytes; |
2287 | |
2288 | frame_bytes = c->cur_frame_bytes; |
2289 | if (frame_bytes <= 0) |
2290 | return c->cur_pts; |
2291 | |
2292 | bytes_left = c->buffer_end - c->buffer_ptr; |
2293 | bytes_sent = frame_bytes - bytes_left; |
2294 | return c->cur_pts + (c->cur_frame_duration * bytes_sent) / frame_bytes; |
2295 | } |
2296 | |
2297 | |
2298 | static int http_prepare_data(HTTPContext *c) |
2299 | { |
2300 | int i, len, ret; |
2301 | AVFormatContext *ctx; |
2302 | |
2303 | av_freep(&c->pb_buffer); |
2304 | switch(c->state) { |
2305 | case HTTPSTATE_SEND_DATA_HEADER: |
2306 | ctx = avformat_alloc_context(); |
2307 | if (!ctx) |
2308 | return AVERROR(ENOMEM); |
2309 | c->pfmt_ctx = ctx; |
2310 | av_dict_copy(&(c->pfmt_ctx->metadata), c->stream->metadata, 0); |
2311 | |
2312 | for(i=0;i<c->stream->nb_streams;i++) { |
2313 | LayeredAVStream *src; |
2314 | AVStream *st = avformat_new_stream(c->pfmt_ctx, NULL); |
2315 | if (!st) |
2316 | return AVERROR(ENOMEM); |
2317 | |
2318 | /* if file or feed, then just take streams from FFServerStream |
2319 | * struct */ |
2320 | if (!c->stream->feed || |
2321 | c->stream->feed == c->stream) |
2322 | src = c->stream->streams[i]; |
2323 | else |
2324 | src = c->stream->feed->streams[c->stream->feed_streams[i]]; |
2325 | |
2326 | unlayer_stream(c->pfmt_ctx->streams[i], src); //TODO we no longer copy st->internal, does this matter? |
2327 | av_assert0(!c->pfmt_ctx->streams[i]->priv_data); |
2328 | |
2329 | if (src->codec->flags & AV_CODEC_FLAG_BITEXACT) |
2330 | c->pfmt_ctx->flags |= AVFMT_FLAG_BITEXACT; |
2331 | } |
2332 | /* set output format parameters */ |
2333 | c->pfmt_ctx->oformat = c->stream->fmt; |
2334 | av_assert0(c->pfmt_ctx->nb_streams == c->stream->nb_streams); |
2335 | |
2336 | c->got_key_frame = 0; |
2337 | |
2338 | /* prepare header and save header data in a stream */ |
2339 | if (avio_open_dyn_buf(&c->pfmt_ctx->pb) < 0) { |
2340 | /* XXX: potential leak */ |
2341 | return -1; |
2342 | } |
2343 | c->pfmt_ctx->pb->seekable = 0; |
2344 | |
2345 | /* |
2346 | * HACK to avoid MPEG-PS muxer to spit many underflow errors |
2347 | * Default value from FFmpeg |
2348 | * Try to set it using configuration option |
2349 | */ |
2350 | c->pfmt_ctx->max_delay = (int)(0.7*AV_TIME_BASE); |
2351 | |
2352 | if ((ret = avformat_write_header(c->pfmt_ctx, NULL)) < 0) { |
2353 | http_log("Error writing output header for stream '%s': %s\n", |
2354 | c->stream->filename, av_err2str(ret)); |
2355 | return ret; |
2356 | } |
2357 | av_dict_free(&c->pfmt_ctx->metadata); |
2358 | |
2359 | len = avio_close_dyn_buf(c->pfmt_ctx->pb, &c->pb_buffer); |
2360 | c->buffer_ptr = c->pb_buffer; |
2361 | c->buffer_end = c->pb_buffer + len; |
2362 | |
2363 | c->state = HTTPSTATE_SEND_DATA; |
2364 | c->last_packet_sent = 0; |
2365 | break; |
2366 | case HTTPSTATE_SEND_DATA: |
2367 | /* find a new packet */ |
2368 | /* read a packet from the input stream */ |
2369 | if (c->stream->feed) |
2370 | ffm_set_write_index(c->fmt_in, |
2371 | c->stream->feed->feed_write_index, |
2372 | c->stream->feed->feed_size); |
2373 | |
2374 | if (c->stream->max_time && |
2375 | c->stream->max_time + c->start_time - cur_time < 0) |
2376 | /* We have timed out */ |
2377 | c->state = HTTPSTATE_SEND_DATA_TRAILER; |
2378 | else { |
2379 | AVPacket pkt; |
2380 | redo: |
2381 | ret = av_read_frame(c->fmt_in, &pkt); |
2382 | if (ret < 0) { |
2383 | if (c->stream->feed) { |
2384 | /* if coming from feed, it means we reached the end of the |
2385 | * ffm file, so must wait for more data */ |
2386 | c->state = HTTPSTATE_WAIT_FEED; |
2387 | return 1; /* state changed */ |
2388 | } |
2389 | if (ret == AVERROR(EAGAIN)) { |
2390 | /* input not ready, come back later */ |
2391 | return 0; |
2392 | } |
2393 | if (c->stream->loop) { |
2394 | avformat_close_input(&c->fmt_in); |
2395 | if (open_input_stream(c, "") < 0) |
2396 | goto no_loop; |
2397 | goto redo; |
2398 | } else { |
2399 | no_loop: |
2400 | /* must send trailer now because EOF or error */ |
2401 | c->state = HTTPSTATE_SEND_DATA_TRAILER; |
2402 | } |
2403 | } else { |
2404 | int source_index = pkt.stream_index; |
2405 | /* update first pts if needed */ |
2406 | if (c->first_pts == AV_NOPTS_VALUE && pkt.dts != AV_NOPTS_VALUE) { |
2407 | c->first_pts = av_rescale_q(pkt.dts, c->fmt_in->streams[pkt.stream_index]->time_base, AV_TIME_BASE_Q); |
2408 | c->start_time = cur_time; |
2409 | } |
2410 | /* send it to the appropriate stream */ |
2411 | if (c->stream->feed) { |
2412 | /* if coming from a feed, select the right stream */ |
2413 | if (c->switch_pending) { |
2414 | c->switch_pending = 0; |
2415 | for(i=0;i<c->stream->nb_streams;i++) { |
2416 | if (c->switch_feed_streams[i] == pkt.stream_index) |
2417 | if (pkt.flags & AV_PKT_FLAG_KEY) |
2418 | c->switch_feed_streams[i] = -1; |
2419 | if (c->switch_feed_streams[i] >= 0) |
2420 | c->switch_pending = 1; |
2421 | } |
2422 | } |
2423 | for(i=0;i<c->stream->nb_streams;i++) { |
2424 | if (c->stream->feed_streams[i] == pkt.stream_index) { |
2425 | AVStream *st = c->fmt_in->streams[source_index]; |
2426 | pkt.stream_index = i; |
2427 | if (pkt.flags & AV_PKT_FLAG_KEY && |
2428 | (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO || |
2429 | c->stream->nb_streams == 1)) |
2430 | c->got_key_frame = 1; |
2431 | if (!c->stream->send_on_key || c->got_key_frame) |
2432 | goto send_it; |
2433 | } |
2434 | } |
2435 | } else { |
2436 | AVStream *ist, *ost; |
2437 | send_it: |
2438 | ist = c->fmt_in->streams[source_index]; |
2439 | /* specific handling for RTP: we use several |
2440 | * output streams (one for each RTP connection). |
2441 | * XXX: need more abstract handling */ |
2442 | if (c->is_packetized) { |
2443 | /* compute send time and duration */ |
2444 | if (pkt.dts != AV_NOPTS_VALUE) { |
2445 | c->cur_pts = av_rescale_q(pkt.dts, ist->time_base, AV_TIME_BASE_Q); |
2446 | c->cur_pts -= c->first_pts; |
2447 | } |
2448 | c->cur_frame_duration = av_rescale_q(pkt.duration, ist->time_base, AV_TIME_BASE_Q); |
2449 | /* find RTP context */ |
2450 | c->packet_stream_index = pkt.stream_index; |
2451 | ctx = c->rtp_ctx[c->packet_stream_index]; |
2452 | if(!ctx) { |
2453 | av_packet_unref(&pkt); |
2454 | break; |
2455 | } |
2456 | /* only one stream per RTP connection */ |
2457 | pkt.stream_index = 0; |
2458 | } else { |
2459 | ctx = c->pfmt_ctx; |
2460 | /* Fudge here */ |
2461 | } |
2462 | |
2463 | if (c->is_packetized) { |
2464 | int max_packet_size; |
2465 | if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP) |
2466 | max_packet_size = RTSP_TCP_MAX_PACKET_SIZE; |
2467 | else |
2468 | max_packet_size = c->rtp_handles[c->packet_stream_index]->max_packet_size; |
2469 | ret = ffio_open_dyn_packet_buf(&ctx->pb, |
2470 | max_packet_size); |
2471 | } else |
2472 | ret = avio_open_dyn_buf(&ctx->pb); |
2473 | |
2474 | if (ret < 0) { |
2475 | /* XXX: potential leak */ |
2476 | return -1; |
2477 | } |
2478 | ost = ctx->streams[pkt.stream_index]; |
2479 | |
2480 | ctx->pb->seekable = 0; |
2481 | if (pkt.dts != AV_NOPTS_VALUE) |
2482 | pkt.dts = av_rescale_q(pkt.dts, ist->time_base, |
2483 | ost->time_base); |
2484 | if (pkt.pts != AV_NOPTS_VALUE) |
2485 | pkt.pts = av_rescale_q(pkt.pts, ist->time_base, |
2486 | ost->time_base); |
2487 | pkt.duration = av_rescale_q(pkt.duration, ist->time_base, |
2488 | ost->time_base); |
2489 | if ((ret = av_write_frame(ctx, &pkt)) < 0) { |
2490 | http_log("Error writing frame to output for stream '%s': %s\n", |
2491 | c->stream->filename, av_err2str(ret)); |
2492 | c->state = HTTPSTATE_SEND_DATA_TRAILER; |
2493 | } |
2494 | |
2495 | av_freep(&c->pb_buffer); |
2496 | len = avio_close_dyn_buf(ctx->pb, &c->pb_buffer); |
2497 | ctx->pb = NULL; |
2498 | c->cur_frame_bytes = len; |
2499 | c->buffer_ptr = c->pb_buffer; |
2500 | c->buffer_end = c->pb_buffer + len; |
2501 | |
2502 | if (len == 0) { |
2503 | av_packet_unref(&pkt); |
2504 | goto redo; |
2505 | } |
2506 | } |
2507 | av_packet_unref(&pkt); |
2508 | } |
2509 | } |
2510 | break; |
2511 | default: |
2512 | case HTTPSTATE_SEND_DATA_TRAILER: |
2513 | /* last packet test ? */ |
2514 | if (c->last_packet_sent || c->is_packetized) |
2515 | return -1; |
2516 | ctx = c->pfmt_ctx; |
2517 | /* prepare header */ |
2518 | if (avio_open_dyn_buf(&ctx->pb) < 0) { |
2519 | /* XXX: potential leak */ |
2520 | return -1; |
2521 | } |
2522 | c->pfmt_ctx->pb->seekable = 0; |
2523 | av_write_trailer(ctx); |
2524 | len = avio_close_dyn_buf(ctx->pb, &c->pb_buffer); |
2525 | c->buffer_ptr = c->pb_buffer; |
2526 | c->buffer_end = c->pb_buffer + len; |
2527 | |
2528 | c->last_packet_sent = 1; |
2529 | break; |
2530 | } |
2531 | return 0; |
2532 | } |
2533 | |
2534 | /* should convert the format at the same time */ |
2535 | /* send data starting at c->buffer_ptr to the output connection |
2536 | * (either UDP or TCP) |
2537 | */ |
2538 | static int http_send_data(HTTPContext *c) |
2539 | { |
2540 | int len, ret; |
2541 | |
2542 | for(;;) { |
2543 | if (c->buffer_ptr >= c->buffer_end) { |
2544 | ret = http_prepare_data(c); |
2545 | if (ret < 0) |
2546 | return -1; |
2547 | else if (ret) |
2548 | /* state change requested */ |
2549 | break; |
2550 | } else { |
2551 | if (c->is_packetized) { |
2552 | /* RTP data output */ |
2553 | len = c->buffer_end - c->buffer_ptr; |
2554 | if (len < 4) { |
2555 | /* fail safe - should never happen */ |
2556 | fail1: |
2557 | c->buffer_ptr = c->buffer_end; |
2558 | return 0; |
2559 | } |
2560 | len = (c->buffer_ptr[0] << 24) | |
2561 | (c->buffer_ptr[1] << 16) | |
2562 | (c->buffer_ptr[2] << 8) | |
2563 | (c->buffer_ptr[3]); |
2564 | if (len > (c->buffer_end - c->buffer_ptr)) |
2565 | goto fail1; |
2566 | if ((get_packet_send_clock(c) - get_server_clock(c)) > 0) { |
2567 | /* nothing to send yet: we can wait */ |
2568 | return 0; |
2569 | } |
2570 | |
2571 | c->data_count += len; |
2572 | update_datarate(&c->datarate, c->data_count); |
2573 | if (c->stream) |
2574 | c->stream->bytes_served += len; |
2575 | |
2576 | if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP) { |
2577 | /* RTP packets are sent inside the RTSP TCP connection */ |
2578 | AVIOContext *pb; |
2579 | int interleaved_index, size; |
2580 | uint8_t header[4]; |
2581 | HTTPContext *rtsp_c; |
2582 | |
2583 | rtsp_c = c->rtsp_c; |
2584 | /* if no RTSP connection left, error */ |
2585 | if (!rtsp_c) |
2586 | return -1; |
2587 | /* if already sending something, then wait. */ |
2588 | if (rtsp_c->state != RTSPSTATE_WAIT_REQUEST) |
2589 | break; |
2590 | if (avio_open_dyn_buf(&pb) < 0) |
2591 | goto fail1; |
2592 | interleaved_index = c->packet_stream_index * 2; |
2593 | /* RTCP packets are sent at odd indexes */ |
2594 | if (c->buffer_ptr[1] == 200) |
2595 | interleaved_index++; |
2596 | /* write RTSP TCP header */ |
2597 | header[0] = '$'; |
2598 | header[1] = interleaved_index; |
2599 | header[2] = len >> 8; |
2600 | header[3] = len; |
2601 | avio_write(pb, header, 4); |
2602 | /* write RTP packet data */ |
2603 | c->buffer_ptr += 4; |
2604 | avio_write(pb, c->buffer_ptr, len); |
2605 | size = avio_close_dyn_buf(pb, &c->packet_buffer); |
2606 | /* prepare asynchronous TCP sending */ |
2607 | rtsp_c->packet_buffer_ptr = c->packet_buffer; |
2608 | rtsp_c->packet_buffer_end = c->packet_buffer + size; |
2609 | c->buffer_ptr += len; |
2610 | |
2611 | /* send everything we can NOW */ |
2612 | len = send(rtsp_c->fd, rtsp_c->packet_buffer_ptr, |
2613 | rtsp_c->packet_buffer_end - rtsp_c->packet_buffer_ptr, 0); |
2614 | if (len > 0) |
2615 | rtsp_c->packet_buffer_ptr += len; |
2616 | if (rtsp_c->packet_buffer_ptr < rtsp_c->packet_buffer_end) { |
2617 | /* if we could not send all the data, we will |
2618 | * send it later, so a new state is needed to |
2619 | * "lock" the RTSP TCP connection */ |
2620 | rtsp_c->state = RTSPSTATE_SEND_PACKET; |
2621 | break; |
2622 | } else |
2623 | /* all data has been sent */ |
2624 | av_freep(&c->packet_buffer); |
2625 | } else { |
2626 | /* send RTP packet directly in UDP */ |
2627 | c->buffer_ptr += 4; |
2628 | ffurl_write(c->rtp_handles[c->packet_stream_index], |
2629 | c->buffer_ptr, len); |
2630 | c->buffer_ptr += len; |
2631 | /* here we continue as we can send several packets |
2632 | * per 10 ms slot */ |
2633 | } |
2634 | } else { |
2635 | /* TCP data output */ |
2636 | len = send(c->fd, c->buffer_ptr, |
2637 | c->buffer_end - c->buffer_ptr, 0); |
2638 | if (len < 0) { |
2639 | if (ff_neterrno() != AVERROR(EAGAIN) && |
2640 | ff_neterrno() != AVERROR(EINTR)) |
2641 | /* error : close connection */ |
2642 | return -1; |
2643 | else |
2644 | return 0; |
2645 | } |
2646 | c->buffer_ptr += len; |
2647 | |
2648 | c->data_count += len; |
2649 | update_datarate(&c->datarate, c->data_count); |
2650 | if (c->stream) |
2651 | c->stream->bytes_served += len; |
2652 | break; |
2653 | } |
2654 | } |
2655 | } /* for(;;) */ |
2656 | return 0; |
2657 | } |
2658 | |
2659 | static int http_start_receive_data(HTTPContext *c) |
2660 | { |
2661 | int fd; |
2662 | int ret; |
2663 | int64_t ret64; |
2664 | |
2665 | if (c->stream->feed_opened) { |
2666 | http_log("Stream feed '%s' was not opened\n", |
2667 | c->stream->feed_filename); |
2668 | return AVERROR(EINVAL); |
2669 | } |
2670 | |
2671 | /* Don't permit writing to this one */ |
2672 | if (c->stream->readonly) { |
2673 | http_log("Cannot write to read-only file '%s'\n", |
2674 | c->stream->feed_filename); |
2675 | return AVERROR(EINVAL); |
2676 | } |
2677 | |
2678 | /* open feed */ |
2679 | fd = open(c->stream->feed_filename, O_RDWR); |
2680 | if (fd < 0) { |
2681 | ret = AVERROR(errno); |
2682 | http_log("Could not open feed file '%s': %s\n", |
2683 | c->stream->feed_filename, strerror(errno)); |
2684 | return ret; |
2685 | } |
2686 | c->feed_fd = fd; |
2687 | |
2688 | if (c->stream->truncate) { |
2689 | /* truncate feed file */ |
2690 | ffm_write_write_index(c->feed_fd, FFM_PACKET_SIZE); |
2691 | http_log("Truncating feed file '%s'\n", c->stream->feed_filename); |
2692 | if (ftruncate(c->feed_fd, FFM_PACKET_SIZE) < 0) { |
2693 | ret = AVERROR(errno); |
2694 | http_log("Error truncating feed file '%s': %s\n", |
2695 | c->stream->feed_filename, strerror(errno)); |
2696 | return ret; |
2697 | } |
2698 | } else { |
2699 | ret64 = ffm_read_write_index(fd); |
2700 | if (ret64 < 0) { |
2701 | http_log("Error reading write index from feed file '%s': %s\n", |
2702 | c->stream->feed_filename, strerror(errno)); |
2703 | return ret64; |
2704 | } |
2705 | c->stream->feed_write_index = ret64; |
2706 | } |
2707 | |
2708 | c->stream->feed_write_index = FFMAX(ffm_read_write_index(fd), |
2709 | FFM_PACKET_SIZE); |
2710 | c->stream->feed_size = lseek(fd, 0, SEEK_END); |
2711 | lseek(fd, 0, SEEK_SET); |
2712 | |
2713 | /* init buffer input */ |
2714 | c->buffer_ptr = c->buffer; |
2715 | c->buffer_end = c->buffer + FFM_PACKET_SIZE; |
2716 | c->stream->feed_opened = 1; |
2717 | c->chunked_encoding = !!av_stristr(c->buffer, "Transfer-Encoding: chunked"); |
2718 | return 0; |
2719 | } |
2720 | |
2721 | static int http_receive_data(HTTPContext *c) |
2722 | { |
2723 | HTTPContext *c1; |
2724 | int len, loop_run = 0; |
2725 | |
2726 | while (c->chunked_encoding && !c->chunk_size && |
2727 | c->buffer_end > c->buffer_ptr) { |
2728 | /* read chunk header, if present */ |
2729 | len = recv(c->fd, c->buffer_ptr, 1, 0); |
2730 | |
2731 | if (len < 0) { |
2732 | if (ff_neterrno() != AVERROR(EAGAIN) && |
2733 | ff_neterrno() != AVERROR(EINTR)) |
2734 | /* error : close connection */ |
2735 | goto fail; |
2736 | return 0; |
2737 | } else if (len == 0) { |
2738 | /* end of connection : close it */ |
2739 | goto fail; |
2740 | } else if (c->buffer_ptr - c->buffer >= 2 && |
2741 | !memcmp(c->buffer_ptr - 1, "\r\n", 2)) { |
2742 | c->chunk_size = strtol(c->buffer, 0, 16); |
2743 | if (c->chunk_size <= 0) { // end of stream or invalid chunk size |
2744 | c->chunk_size = 0; |
2745 | goto fail; |
2746 | } |
2747 | c->buffer_ptr = c->buffer; |
2748 | break; |
2749 | } else if (++loop_run > 10) |
2750 | /* no chunk header, abort */ |
2751 | goto fail; |
2752 | else |
2753 | c->buffer_ptr++; |
2754 | } |
2755 | |
2756 | if (c->buffer_end > c->buffer_ptr) { |
2757 | len = recv(c->fd, c->buffer_ptr, |
2758 | FFMIN(c->chunk_size, c->buffer_end - c->buffer_ptr), 0); |
2759 | if (len < 0) { |
2760 | if (ff_neterrno() != AVERROR(EAGAIN) && |
2761 | ff_neterrno() != AVERROR(EINTR)) |
2762 | /* error : close connection */ |
2763 | goto fail; |
2764 | } else if (len == 0) |
2765 | /* end of connection : close it */ |
2766 | goto fail; |
2767 | else { |
2768 | av_assert0(len <= c->chunk_size); |
2769 | c->chunk_size -= len; |
2770 | c->buffer_ptr += len; |
2771 | c->data_count += len; |
2772 | update_datarate(&c->datarate, c->data_count); |
2773 | } |
2774 | } |
2775 | |
2776 | if (c->buffer_ptr - c->buffer >= 2 && c->data_count > FFM_PACKET_SIZE) { |
2777 | if (c->buffer[0] != 'f' || |
2778 | c->buffer[1] != 'm') { |
2779 | http_log("Feed stream has become desynchronized -- disconnecting\n"); |
2780 | goto fail; |
2781 | } |
2782 | } |
2783 | |
2784 | if (c->buffer_ptr >= c->buffer_end) { |
2785 | FFServerStream *feed = c->stream; |
2786 | /* a packet has been received : write it in the store, except |
2787 | * if header */ |
2788 | if (c->data_count > FFM_PACKET_SIZE) { |
2789 | /* XXX: use llseek or url_seek |
2790 | * XXX: Should probably fail? */ |
2791 | if (lseek(c->feed_fd, feed->feed_write_index, SEEK_SET) == -1) |
2792 | http_log("Seek to %"PRId64" failed\n", feed->feed_write_index); |
2793 | |
2794 | if (write(c->feed_fd, c->buffer, FFM_PACKET_SIZE) < 0) { |
2795 | http_log("Error writing to feed file: %s\n", strerror(errno)); |
2796 | goto fail; |
2797 | } |
2798 | |
2799 | feed->feed_write_index += FFM_PACKET_SIZE; |
2800 | /* update file size */ |
2801 | if (feed->feed_write_index > c->stream->feed_size) |
2802 | feed->feed_size = feed->feed_write_index; |
2803 | |
2804 | /* handle wrap around if max file size reached */ |
2805 | if (c->stream->feed_max_size && |
2806 | feed->feed_write_index >= c->stream->feed_max_size) |
2807 | feed->feed_write_index = FFM_PACKET_SIZE; |
2808 | |
2809 | /* write index */ |
2810 | if (ffm_write_write_index(c->feed_fd, feed->feed_write_index) < 0) { |
2811 | http_log("Error writing index to feed file: %s\n", |
2812 | strerror(errno)); |
2813 | goto fail; |
2814 | } |
2815 | |
2816 | /* wake up any waiting connections */ |
2817 | for(c1 = first_http_ctx; c1; c1 = c1->next) { |
2818 | if (c1->state == HTTPSTATE_WAIT_FEED && |
2819 | c1->stream->feed == c->stream->feed) |
2820 | c1->state = HTTPSTATE_SEND_DATA; |
2821 | } |
2822 | } else { |
2823 | /* We have a header in our hands that contains useful data */ |
2824 | AVFormatContext *s = avformat_alloc_context(); |
2825 | AVIOContext *pb; |
2826 | AVInputFormat *fmt_in; |
2827 | int i; |
2828 | |
2829 | if (!s) |
2830 | goto fail; |
2831 | |
2832 | /* use feed output format name to find corresponding input format */ |
2833 | fmt_in = av_find_input_format(feed->fmt->name); |
2834 | if (!fmt_in) |
2835 | goto fail; |
2836 | |
2837 | pb = avio_alloc_context(c->buffer, c->buffer_end - c->buffer, |
2838 | 0, NULL, NULL, NULL, NULL); |
2839 | if (!pb) |
2840 | goto fail; |
2841 | |
2842 | pb->seekable = 0; |
2843 | |
2844 | s->pb = pb; |
2845 | if (avformat_open_input(&s, c->stream->feed_filename, fmt_in, NULL) < 0) { |
2846 | av_freep(&pb); |
2847 | goto fail; |
2848 | } |
2849 | |
2850 | /* Now we have the actual streams */ |
2851 | if (s->nb_streams != feed->nb_streams) { |
2852 | avformat_close_input(&s); |
2853 | av_freep(&pb); |
2854 | http_log("Feed '%s' stream number does not match registered feed\n", |
2855 | c->stream->feed_filename); |
2856 | goto fail; |
2857 | } |
2858 | |
2859 | for (i = 0; i < s->nb_streams; i++) { |
2860 | LayeredAVStream *fst = feed->streams[i]; |
2861 | AVStream *st = s->streams[i]; |
2862 | avcodec_parameters_to_context(fst->codec, st->codecpar); |
2863 | avcodec_parameters_from_context(fst->codecpar, fst->codec); |
2864 | } |
2865 | |
2866 | avformat_close_input(&s); |
2867 | av_freep(&pb); |
2868 | } |
2869 | c->buffer_ptr = c->buffer; |
2870 | } |
2871 | |
2872 | return 0; |
2873 | fail: |
2874 | c->stream->feed_opened = 0; |
2875 | close(c->feed_fd); |
2876 | /* wake up any waiting connections to stop waiting for feed */ |
2877 | for(c1 = first_http_ctx; c1; c1 = c1->next) { |
2878 | if (c1->state == HTTPSTATE_WAIT_FEED && |
2879 | c1->stream->feed == c->stream->feed) |
2880 | c1->state = HTTPSTATE_SEND_DATA_TRAILER; |
2881 | } |
2882 | return -1; |
2883 | } |
2884 | |
2885 | /********************************************************************/ |
2886 | /* RTSP handling */ |
2887 | |
2888 | static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number) |
2889 | { |
2890 | const char *str; |
2891 | time_t ti; |
2892 | struct tm *tm; |
2893 | char buf2[32]; |
2894 | |
2895 | str = RTSP_STATUS_CODE2STRING(error_number); |
2896 | if (!str) |
2897 | str = "Unknown Error"; |
2898 | |
2899 | avio_printf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str); |
2900 | avio_printf(c->pb, "CSeq: %d\r\n", c->seq); |
2901 | |
2902 | /* output GMT time */ |
2903 | ti = time(NULL); |
2904 | tm = gmtime(&ti); |
2905 | strftime(buf2, sizeof(buf2), "%a, %d %b %Y %H:%M:%S", tm); |
2906 | avio_printf(c->pb, "Date: %s GMT\r\n", buf2); |
2907 | } |
2908 | |
2909 | static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number) |
2910 | { |
2911 | rtsp_reply_header(c, error_number); |
2912 | avio_printf(c->pb, "\r\n"); |
2913 | } |
2914 | |
2915 | static int rtsp_parse_request(HTTPContext *c) |
2916 | { |
2917 | const char *p, *p1, *p2; |
2918 | char cmd[32]; |
2919 | char url[1024]; |
2920 | char protocol[32]; |
2921 | char line[1024]; |
2922 | int len; |
2923 | RTSPMessageHeader header1 = { 0 }, *header = &header1; |
2924 | |
2925 | c->buffer_ptr[0] = '\0'; |
2926 | p = c->buffer; |
2927 | |
2928 | get_word(cmd, sizeof(cmd), &p); |
2929 | get_word(url, sizeof(url), &p); |
2930 | get_word(protocol, sizeof(protocol), &p); |
2931 | |
2932 | av_strlcpy(c->method, cmd, sizeof(c->method)); |
2933 | av_strlcpy(c->url, url, sizeof(c->url)); |
2934 | av_strlcpy(c->protocol, protocol, sizeof(c->protocol)); |
2935 | |
2936 | if (avio_open_dyn_buf(&c->pb) < 0) { |
2937 | /* XXX: cannot do more */ |
2938 | c->pb = NULL; /* safety */ |
2939 | return -1; |
2940 | } |
2941 | |
2942 | /* check version name */ |
2943 | if (strcmp(protocol, "RTSP/1.0")) { |
2944 | rtsp_reply_error(c, RTSP_STATUS_VERSION); |
2945 | goto the_end; |
2946 | } |
2947 | |
2948 | /* parse each header line */ |
2949 | /* skip to next line */ |
2950 | while (*p != '\n' && *p != '\0') |
2951 | p++; |
2952 | if (*p == '\n') |
2953 | p++; |
2954 | while (*p != '\0') { |
2955 | p1 = memchr(p, '\n', (char *)c->buffer_ptr - p); |
2956 | if (!p1) |
2957 | break; |
2958 | p2 = p1; |
2959 | if (p2 > p && p2[-1] == '\r') |
2960 | p2--; |
2961 | /* skip empty line */ |
2962 | if (p2 == p) |
2963 | break; |
2964 | len = p2 - p; |
2965 | if (len > sizeof(line) - 1) |
2966 | len = sizeof(line) - 1; |
2967 | memcpy(line, p, len); |
2968 | line[len] = '\0'; |
2969 | ff_rtsp_parse_line(NULL, header, line, NULL, NULL); |
2970 | p = p1 + 1; |
2971 | } |
2972 | |
2973 | /* handle sequence number */ |
2974 | c->seq = header->seq; |
2975 | |
2976 | if (!strcmp(cmd, "DESCRIBE")) |
2977 | rtsp_cmd_describe(c, url); |
2978 | else if (!strcmp(cmd, "OPTIONS")) |
2979 | rtsp_cmd_options(c, url); |
2980 | else if (!strcmp(cmd, "SETUP")) |
2981 | rtsp_cmd_setup(c, url, header); |
2982 | else if (!strcmp(cmd, "PLAY")) |
2983 | rtsp_cmd_play(c, url, header); |
2984 | else if (!strcmp(cmd, "PAUSE")) |
2985 | rtsp_cmd_interrupt(c, url, header, 1); |
2986 | else if (!strcmp(cmd, "TEARDOWN")) |
2987 | rtsp_cmd_interrupt(c, url, header, 0); |
2988 | else |
2989 | rtsp_reply_error(c, RTSP_STATUS_METHOD); |
2990 | |
2991 | the_end: |
2992 | len = avio_close_dyn_buf(c->pb, &c->pb_buffer); |
2993 | c->pb = NULL; /* safety */ |
2994 | if (len < 0) |
2995 | /* XXX: cannot do more */ |
2996 | return -1; |
2997 | |
2998 | c->buffer_ptr = c->pb_buffer; |
2999 | c->buffer_end = c->pb_buffer + len; |
3000 | c->state = RTSPSTATE_SEND_REPLY; |
3001 | return 0; |
3002 | } |
3003 | |
3004 | static int prepare_sdp_description(FFServerStream *stream, uint8_t **pbuffer, |
3005 | struct in_addr my_ip) |
3006 | { |
3007 | AVFormatContext *avc; |
3008 | AVOutputFormat *rtp_format = av_guess_format("rtp", NULL, NULL); |
3009 | AVDictionaryEntry *entry = av_dict_get(stream->metadata, "title", NULL, 0); |
3010 | int i; |
3011 | |
3012 | *pbuffer = NULL; |
3013 | |
3014 | avc = avformat_alloc_context(); |
3015 | if (!avc || !rtp_format) |
3016 | return -1; |
3017 | |
3018 | avc->oformat = rtp_format; |
3019 | av_dict_set(&avc->metadata, "title", |
3020 | entry ? entry->value : "No Title", 0); |
3021 | if (stream->is_multicast) { |
3022 | snprintf(avc->filename, 1024, "rtp://%s:%d?multicast=1?ttl=%d", |
3023 | inet_ntoa(stream->multicast_ip), |
3024 | stream->multicast_port, stream->multicast_ttl); |
3025 | } else |
3026 | snprintf(avc->filename, 1024, "rtp://0.0.0.0"); |
3027 | |
3028 | for(i = 0; i < stream->nb_streams; i++) { |
3029 | AVStream *st = avformat_new_stream(avc, NULL); |
3030 | if (!st) |
3031 | goto sdp_done; |
3032 | avcodec_parameters_from_context(stream->streams[i]->codecpar, stream->streams[i]->codec); |
3033 | unlayer_stream(st, stream->streams[i]); |
3034 | } |
3035 | #define PBUFFER_SIZE 2048 |
3036 | *pbuffer = av_mallocz(PBUFFER_SIZE); |
3037 | if (!*pbuffer) |
3038 | goto sdp_done; |
3039 | av_sdp_create(&avc, 1, *pbuffer, PBUFFER_SIZE); |
3040 | |
3041 | sdp_done: |
3042 | av_freep(&avc->streams); |
3043 | av_dict_free(&avc->metadata); |
3044 | av_free(avc); |
3045 | |
3046 | return *pbuffer ? strlen(*pbuffer) : AVERROR(ENOMEM); |
3047 | } |
3048 | |
3049 | static void rtsp_cmd_options(HTTPContext *c, const char *url) |
3050 | { |
3051 | /* rtsp_reply_header(c, RTSP_STATUS_OK); */ |
3052 | avio_printf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK"); |
3053 | avio_printf(c->pb, "CSeq: %d\r\n", c->seq); |
3054 | avio_printf(c->pb, "Public: %s\r\n", |
3055 | "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE"); |
3056 | avio_printf(c->pb, "\r\n"); |
3057 | } |
3058 | |
3059 | static void rtsp_cmd_describe(HTTPContext *c, const char *url) |
3060 | { |
3061 | FFServerStream *stream; |
3062 | char path1[1024]; |
3063 | const char *path; |
3064 | uint8_t *content; |
3065 | int content_length; |
3066 | socklen_t len; |
3067 | struct sockaddr_in my_addr; |
3068 | |
3069 | /* find which URL is asked */ |
3070 | av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url); |
3071 | path = path1; |
3072 | if (*path == '/') |
3073 | path++; |
3074 | |
3075 | for(stream = config.first_stream; stream; stream = stream->next) { |
3076 | if (!stream->is_feed && |
3077 | stream->fmt && !strcmp(stream->fmt->name, "rtp") && |
3078 | !strcmp(path, stream->filename)) { |
3079 | goto found; |
3080 | } |
3081 | } |
3082 | /* no stream found */ |
3083 | rtsp_reply_error(c, RTSP_STATUS_NOT_FOUND); |
3084 | return; |
3085 | |
3086 | found: |
3087 | /* prepare the media description in SDP format */ |
3088 | |
3089 | /* get the host IP */ |
3090 | len = sizeof(my_addr); |
3091 | getsockname(c->fd, (struct sockaddr *)&my_addr, &len); |
3092 | content_length = prepare_sdp_description(stream, &content, |
3093 | my_addr.sin_addr); |
3094 | if (content_length < 0) { |
3095 | rtsp_reply_error(c, RTSP_STATUS_INTERNAL); |
3096 | return; |
3097 | } |
3098 | rtsp_reply_header(c, RTSP_STATUS_OK); |
3099 | avio_printf(c->pb, "Content-Base: %s/\r\n", url); |
3100 | avio_printf(c->pb, "Content-Type: application/sdp\r\n"); |
3101 | avio_printf(c->pb, "Content-Length: %d\r\n", content_length); |
3102 | avio_printf(c->pb, "\r\n"); |
3103 | avio_write(c->pb, content, content_length); |
3104 | av_free(content); |
3105 | } |
3106 | |
3107 | static HTTPContext *find_rtp_session(const char *session_id) |
3108 | { |
3109 | HTTPContext *c; |
3110 | |
3111 | if (session_id[0] == '\0') |
3112 | return NULL; |
3113 | |
3114 | for(c = first_http_ctx; c; c = c->next) { |
3115 | if (!strcmp(c->session_id, session_id)) |
3116 | return c; |
3117 | } |
3118 | return NULL; |
3119 | } |
3120 | |
3121 | static RTSPTransportField *find_transport(RTSPMessageHeader *h, enum RTSPLowerTransport lower_transport) |
3122 | { |
3123 | RTSPTransportField *th; |
3124 | int i; |
3125 | |
3126 | for(i=0;i<h->nb_transports;i++) { |
3127 | th = &h->transports[i]; |
3128 | if (th->lower_transport == lower_transport) |
3129 | return th; |
3130 | } |
3131 | return NULL; |
3132 | } |
3133 | |
3134 | static void rtsp_cmd_setup(HTTPContext *c, const char *url, |
3135 | RTSPMessageHeader *h) |
3136 | { |
3137 | FFServerStream *stream; |
3138 | int stream_index, rtp_port, rtcp_port; |
3139 | char buf[1024]; |
3140 | char path1[1024]; |
3141 | const char *path; |
3142 | HTTPContext *rtp_c; |
3143 | RTSPTransportField *th; |
3144 | struct sockaddr_in dest_addr; |
3145 | RTSPActionServerSetup setup; |
3146 | |
3147 | /* find which URL is asked */ |
3148 | av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url); |
3149 | path = path1; |
3150 | if (*path == '/') |
3151 | path++; |
3152 | |
3153 | /* now check each stream */ |
3154 | for(stream = config.first_stream; stream; stream = stream->next) { |
3155 | if (stream->is_feed || !stream->fmt || |
3156 | strcmp(stream->fmt->name, "rtp")) { |
3157 | continue; |
3158 | } |
3159 | /* accept aggregate filenames only if single stream */ |
3160 | if (!strcmp(path, stream->filename)) { |
3161 | if (stream->nb_streams != 1) { |
3162 | rtsp_reply_error(c, RTSP_STATUS_AGGREGATE); |
3163 | return; |
3164 | } |
3165 | stream_index = 0; |
3166 | goto found; |
3167 | } |
3168 | |
3169 | for(stream_index = 0; stream_index < stream->nb_streams; |
3170 | stream_index++) { |
3171 | snprintf(buf, sizeof(buf), "%s/streamid=%d", |
3172 | stream->filename, stream_index); |
3173 | if (!strcmp(path, buf)) |
3174 | goto found; |
3175 | } |
3176 | } |
3177 | /* no stream found */ |
3178 | rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */ |
3179 | return; |
3180 | found: |
3181 | |
3182 | /* generate session id if needed */ |
3183 | if (h->session_id[0] == '\0') { |
3184 | unsigned random0 = av_lfg_get(&random_state); |
3185 | unsigned random1 = av_lfg_get(&random_state); |
3186 | snprintf(h->session_id, sizeof(h->session_id), "%08x%08x", |
3187 | random0, random1); |
3188 | } |
3189 | |
3190 | /* find RTP session, and create it if none found */ |
3191 | rtp_c = find_rtp_session(h->session_id); |
3192 | if (!rtp_c) { |
3193 | /* always prefer UDP */ |
3194 | th = find_transport(h, RTSP_LOWER_TRANSPORT_UDP); |
3195 | if (!th) { |
3196 | th = find_transport(h, RTSP_LOWER_TRANSPORT_TCP); |
3197 | if (!th) { |
3198 | rtsp_reply_error(c, RTSP_STATUS_TRANSPORT); |
3199 | return; |
3200 | } |
3201 | } |
3202 | |
3203 | rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id, |
3204 | th->lower_transport); |
3205 | if (!rtp_c) { |
3206 | rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH); |
3207 | return; |
3208 | } |
3209 | |
3210 | /* open input stream */ |
3211 | if (open_input_stream(rtp_c, "") < 0) { |
3212 | rtsp_reply_error(c, RTSP_STATUS_INTERNAL); |
3213 | return; |
3214 | } |
3215 | } |
3216 | |
3217 | /* test if stream is OK (test needed because several SETUP needs |
3218 | * to be done for a given file) */ |
3219 | if (rtp_c->stream != stream) { |
3220 | rtsp_reply_error(c, RTSP_STATUS_SERVICE); |
3221 | return; |
3222 | } |
3223 | |
3224 | /* test if stream is already set up */ |
3225 | if (rtp_c->rtp_ctx[stream_index]) { |
3226 | rtsp_reply_error(c, RTSP_STATUS_STATE); |
3227 | return; |
3228 | } |
3229 | |
3230 | /* check transport */ |
3231 | th = find_transport(h, rtp_c->rtp_protocol); |
3232 | if (!th || (th->lower_transport == RTSP_LOWER_TRANSPORT_UDP && |
3233 | th->client_port_min <= 0)) { |
3234 | rtsp_reply_error(c, RTSP_STATUS_TRANSPORT); |
3235 | return; |
3236 | } |
3237 | |
3238 | /* setup default options */ |
3239 | setup.transport_option[0] = '\0'; |
3240 | dest_addr = rtp_c->from_addr; |
3241 | dest_addr.sin_port = htons(th->client_port_min); |
3242 | |
3243 | /* setup stream */ |
3244 | if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) { |
3245 | rtsp_reply_error(c, RTSP_STATUS_TRANSPORT); |
3246 | return; |
3247 | } |
3248 | |
3249 | /* now everything is OK, so we can send the connection parameters */ |
3250 | rtsp_reply_header(c, RTSP_STATUS_OK); |
3251 | /* session ID */ |
3252 | avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id); |
3253 | |
3254 | switch(rtp_c->rtp_protocol) { |
3255 | case RTSP_LOWER_TRANSPORT_UDP: |
3256 | rtp_port = ff_rtp_get_local_rtp_port(rtp_c->rtp_handles[stream_index]); |
3257 | rtcp_port = ff_rtp_get_local_rtcp_port(rtp_c->rtp_handles[stream_index]); |
3258 | avio_printf(c->pb, "Transport: RTP/AVP/UDP;unicast;" |
3259 | "client_port=%d-%d;server_port=%d-%d", |
3260 | th->client_port_min, th->client_port_max, |
3261 | rtp_port, rtcp_port); |
3262 | break; |
3263 | case RTSP_LOWER_TRANSPORT_TCP: |
3264 | avio_printf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d", |
3265 | stream_index * 2, stream_index * 2 + 1); |
3266 | break; |
3267 | default: |
3268 | break; |
3269 | } |
3270 | if (setup.transport_option[0] != '\0') |
3271 | avio_printf(c->pb, ";%s", setup.transport_option); |
3272 | avio_printf(c->pb, "\r\n"); |
3273 | |
3274 | |
3275 | avio_printf(c->pb, "\r\n"); |
3276 | } |
3277 | |
3278 | |
3279 | /** |
3280 | * find an RTP connection by using the session ID. Check consistency |
3281 | * with filename |
3282 | */ |
3283 | static HTTPContext *find_rtp_session_with_url(const char *url, |
3284 | const char *session_id) |
3285 | { |
3286 | HTTPContext *rtp_c; |
3287 | char path1[1024]; |
3288 | const char *path; |
3289 | char buf[1024]; |
3290 | int s, len; |
3291 | |
3292 | rtp_c = find_rtp_session(session_id); |
3293 | if (!rtp_c) |
3294 | return NULL; |
3295 | |
3296 | /* find which URL is asked */ |
3297 | av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url); |
3298 | path = path1; |
3299 | if (*path == '/') |
3300 | path++; |
3301 | if(!strcmp(path, rtp_c->stream->filename)) return rtp_c; |
3302 | for(s=0; s<rtp_c->stream->nb_streams; ++s) { |
3303 | snprintf(buf, sizeof(buf), "%s/streamid=%d", |
3304 | rtp_c->stream->filename, s); |
3305 | if(!strncmp(path, buf, sizeof(buf))) |
3306 | /* XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE |
3307 | * if nb_streams>1? */ |
3308 | return rtp_c; |
3309 | } |
3310 | len = strlen(path); |
3311 | if (len > 0 && path[len - 1] == '/' && |
3312 | !strncmp(path, rtp_c->stream->filename, len - 1)) |
3313 | return rtp_c; |
3314 | return NULL; |
3315 | } |
3316 | |
3317 | static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPMessageHeader *h) |
3318 | { |
3319 | HTTPContext *rtp_c; |
3320 | |
3321 | rtp_c = find_rtp_session_with_url(url, h->session_id); |
3322 | if (!rtp_c) { |
3323 | rtsp_reply_error(c, RTSP_STATUS_SESSION); |
3324 | return; |
3325 | } |
3326 | |
3327 | if (rtp_c->state != HTTPSTATE_SEND_DATA && |
3328 | rtp_c->state != HTTPSTATE_WAIT_FEED && |
3329 | rtp_c->state != HTTPSTATE_READY) { |
3330 | rtsp_reply_error(c, RTSP_STATUS_STATE); |
3331 | return; |
3332 | } |
3333 | |
3334 | rtp_c->state = HTTPSTATE_SEND_DATA; |
3335 | |
3336 | /* now everything is OK, so we can send the connection parameters */ |
3337 | rtsp_reply_header(c, RTSP_STATUS_OK); |
3338 | /* session ID */ |
3339 | avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id); |
3340 | avio_printf(c->pb, "\r\n"); |
3341 | } |
3342 | |
3343 | static void rtsp_cmd_interrupt(HTTPContext *c, const char *url, |
3344 | RTSPMessageHeader *h, int pause_only) |
3345 | { |
3346 | HTTPContext *rtp_c; |
3347 | |
3348 | rtp_c = find_rtp_session_with_url(url, h->session_id); |
3349 | if (!rtp_c) { |
3350 | rtsp_reply_error(c, RTSP_STATUS_SESSION); |
3351 | return; |
3352 | } |
3353 | |
3354 | if (pause_only) { |
3355 | if (rtp_c->state != HTTPSTATE_SEND_DATA && |
3356 | rtp_c->state != HTTPSTATE_WAIT_FEED) { |
3357 | rtsp_reply_error(c, RTSP_STATUS_STATE); |
3358 | return; |
3359 | } |
3360 | rtp_c->state = HTTPSTATE_READY; |
3361 | rtp_c->first_pts = AV_NOPTS_VALUE; |
3362 | } |
3363 | |
3364 | /* now everything is OK, so we can send the connection parameters */ |
3365 | rtsp_reply_header(c, RTSP_STATUS_OK); |
3366 | /* session ID */ |
3367 | avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id); |
3368 | avio_printf(c->pb, "\r\n"); |
3369 | |
3370 | if (!pause_only) |
3371 | close_connection(rtp_c); |
3372 | } |
3373 | |
3374 | /********************************************************************/ |
3375 | /* RTP handling */ |
3376 | |
3377 | static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr, |
3378 | FFServerStream *stream, |
3379 | const char *session_id, |
3380 | enum RTSPLowerTransport rtp_protocol) |
3381 | { |
3382 | HTTPContext *c = NULL; |
3383 | const char *proto_str; |
3384 | |
3385 | /* XXX: should output a warning page when coming |
3386 | * close to the connection limit */ |
3387 | if (nb_connections >= config.nb_max_connections) |
3388 | goto fail; |
3389 | |
3390 | /* add a new connection */ |
3391 | c = av_mallocz(sizeof(HTTPContext)); |
3392 | if (!c) |
3393 | goto fail; |
3394 | |
3395 | c->fd = -1; |
3396 | c->poll_entry = NULL; |
3397 | c->from_addr = *from_addr; |
3398 | c->buffer_size = IOBUFFER_INIT_SIZE; |
3399 | c->buffer = av_malloc(c->buffer_size); |
3400 | if (!c->buffer) |
3401 | goto fail; |
3402 | nb_connections++; |
3403 | c->stream = stream; |
3404 | av_strlcpy(c->session_id, session_id, sizeof(c->session_id)); |
3405 | c->state = HTTPSTATE_READY; |
3406 | c->is_packetized = 1; |
3407 | c->rtp_protocol = rtp_protocol; |
3408 | |
3409 | /* protocol is shown in statistics */ |
3410 | switch(c->rtp_protocol) { |
3411 | case RTSP_LOWER_TRANSPORT_UDP_MULTICAST: |
3412 | proto_str = "MCAST"; |
3413 | break; |
3414 | case RTSP_LOWER_TRANSPORT_UDP: |
3415 | proto_str = "UDP"; |
3416 | break; |
3417 | case RTSP_LOWER_TRANSPORT_TCP: |
3418 | proto_str = "TCP"; |
3419 | break; |
3420 | default: |
3421 | proto_str = "???"; |
3422 | break; |
3423 | } |
3424 | av_strlcpy(c->protocol, "RTP/", sizeof(c->protocol)); |
3425 | av_strlcat(c->protocol, proto_str, sizeof(c->protocol)); |
3426 | |
3427 | current_bandwidth += stream->bandwidth; |
3428 | |
3429 | c->next = first_http_ctx; |
3430 | first_http_ctx = c; |
3431 | return c; |
3432 | |
3433 | fail: |
3434 | if (c) { |
3435 | av_freep(&c->buffer); |
3436 | av_free(c); |
3437 | } |
3438 | return NULL; |
3439 | } |
3440 | |
3441 | /** |
3442 | * add a new RTP stream in an RTP connection (used in RTSP SETUP |
3443 | * command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is |
3444 | * used. |
3445 | */ |
3446 | static int rtp_new_av_stream(HTTPContext *c, |
3447 | int stream_index, struct sockaddr_in *dest_addr, |
3448 | HTTPContext *rtsp_c) |
3449 | { |
3450 | AVFormatContext *ctx; |
3451 | AVStream *st; |
3452 | char *ipaddr; |
3453 | URLContext *h = NULL; |
3454 | uint8_t *dummy_buf; |
3455 | int max_packet_size; |
3456 | void *st_internal; |
3457 | |
3458 | /* now we can open the relevant output stream */ |
3459 | ctx = avformat_alloc_context(); |
3460 | if (!ctx) |
3461 | return -1; |
3462 | ctx->oformat = av_guess_format("rtp", NULL, NULL); |
3463 | |
3464 | st = avformat_new_stream(ctx, NULL); |
3465 | if (!st) |
3466 | goto fail; |
3467 | |
3468 | st_internal = st->internal; |
3469 | |
3470 | if (!c->stream->feed || |
3471 | c->stream->feed == c->stream) |
3472 | unlayer_stream(st, c->stream->streams[stream_index]); |
3473 | else |
3474 | unlayer_stream(st, |
3475 | c->stream->feed->streams[c->stream->feed_streams[stream_index]]); |
3476 | av_assert0(st->priv_data == NULL); |
3477 | av_assert0(st->internal == st_internal); |
3478 | |
3479 | /* build destination RTP address */ |
3480 | ipaddr = inet_ntoa(dest_addr->sin_addr); |
3481 | |
3482 | switch(c->rtp_protocol) { |
3483 | case RTSP_LOWER_TRANSPORT_UDP: |
3484 | case RTSP_LOWER_TRANSPORT_UDP_MULTICAST: |
3485 | /* RTP/UDP case */ |
3486 | |
3487 | /* XXX: also pass as parameter to function ? */ |
3488 | if (c->stream->is_multicast) { |
3489 | int ttl; |
3490 | ttl = c->stream->multicast_ttl; |
3491 | if (!ttl) |
3492 | ttl = 16; |
3493 | snprintf(ctx->filename, sizeof(ctx->filename), |
3494 | "rtp://%s:%d?multicast=1&ttl=%d", |
3495 | ipaddr, ntohs(dest_addr->sin_port), ttl); |
3496 | } else { |
3497 | snprintf(ctx->filename, sizeof(ctx->filename), |
3498 | "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port)); |
3499 | } |
3500 | |
3501 | if (ffurl_open(&h, ctx->filename, AVIO_FLAG_WRITE, NULL, NULL) < 0) |
3502 | goto fail; |
3503 | c->rtp_handles[stream_index] = h; |
3504 | max_packet_size = h->max_packet_size; |
3505 | break; |
3506 | case RTSP_LOWER_TRANSPORT_TCP: |
3507 | /* RTP/TCP case */ |
3508 | c->rtsp_c = rtsp_c; |
3509 | max_packet_size = RTSP_TCP_MAX_PACKET_SIZE; |
3510 | break; |
3511 | default: |
3512 | goto fail; |
3513 | } |
3514 | |
3515 | http_log("%s:%d - - \"PLAY %s/streamid=%d %s\"\n", |
3516 | ipaddr, ntohs(dest_addr->sin_port), |
3517 | c->stream->filename, stream_index, c->protocol); |
3518 | |
3519 | /* normally, no packets should be output here, but the packet size may |
3520 | * be checked */ |
3521 | if (ffio_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) |
3522 | /* XXX: close stream */ |
3523 | goto fail; |
3524 | |
3525 | if (avformat_write_header(ctx, NULL) < 0) { |
3526 | fail: |
3527 | if (h) |
3528 | ffurl_close(h); |
3529 | av_free(st); |
3530 | av_free(ctx); |
3531 | return -1; |
3532 | } |
3533 | avio_close_dyn_buf(ctx->pb, &dummy_buf); |
3534 | ctx->pb = NULL; |
3535 | av_free(dummy_buf); |
3536 | |
3537 | c->rtp_ctx[stream_index] = ctx; |
3538 | return 0; |
3539 | } |
3540 | |
3541 | /********************************************************************/ |
3542 | /* ffserver initialization */ |
3543 | |
3544 | /* FIXME: This code should use avformat_new_stream() */ |
3545 | static LayeredAVStream *add_av_stream1(FFServerStream *stream, |
3546 | AVCodecContext *codec, int copy) |
3547 | { |
3548 | LayeredAVStream *fst; |
3549 | |
3550 | if(stream->nb_streams >= FF_ARRAY_ELEMS(stream->streams)) |
3551 | return NULL; |
3552 | |
3553 | fst = av_mallocz(sizeof(*fst)); |
3554 | if (!fst) |
3555 | return NULL; |
3556 | if (copy) { |
3557 | fst->codec = avcodec_alloc_context3(codec->codec); |
3558 | if (!fst->codec) { |
3559 | av_free(fst); |
3560 | return NULL; |
3561 | } |
3562 | avcodec_copy_context(fst->codec, codec); |
3563 | } else |
3564 | /* live streams must use the actual feed's codec since it may be |
3565 | * updated later to carry extradata needed by them. |
3566 | */ |
3567 | fst->codec = codec; |
3568 | |
3569 | //NOTE we previously allocated internal & internal->avctx, these seemed uneeded though |
3570 | fst->codecpar = avcodec_parameters_alloc(); |
3571 | fst->index = stream->nb_streams; |
3572 | fst->time_base = codec->time_base; |
3573 | fst->pts_wrap_bits = 33; |
3574 | fst->sample_aspect_ratio = codec->sample_aspect_ratio; |
3575 | stream->streams[stream->nb_streams++] = fst; |
3576 | return fst; |
3577 | } |
3578 | |
3579 | /* return the stream number in the feed */ |
3580 | static int add_av_stream(FFServerStream *feed, LayeredAVStream *st) |
3581 | { |
3582 | LayeredAVStream *fst; |
3583 | AVCodecContext *av, *av1; |
3584 | int i; |
3585 | |
3586 | av = st->codec; |
3587 | for(i=0;i<feed->nb_streams;i++) { |
3588 | av1 = feed->streams[i]->codec; |
3589 | if (av1->codec_id == av->codec_id && |
3590 | av1->codec_type == av->codec_type && |
3591 | av1->bit_rate == av->bit_rate) { |
3592 | |
3593 | switch(av->codec_type) { |
3594 | case AVMEDIA_TYPE_AUDIO: |
3595 | if (av1->channels == av->channels && |
3596 | av1->sample_rate == av->sample_rate) |
3597 | return i; |
3598 | break; |
3599 | case AVMEDIA_TYPE_VIDEO: |
3600 | if (av1->width == av->width && |
3601 | av1->height == av->height && |
3602 | av1->time_base.den == av->time_base.den && |
3603 | av1->time_base.num == av->time_base.num && |
3604 | av1->gop_size == av->gop_size) |
3605 | return i; |
3606 | break; |
3607 | default: |
3608 | abort(); |
3609 | } |
3610 | } |
3611 | } |
3612 | |
3613 | fst = add_av_stream1(feed, av, 0); |
3614 | if (!fst) |
3615 | return -1; |
3616 | if (st->recommended_encoder_configuration) |
3617 | fst->recommended_encoder_configuration = |
3618 | av_strdup(st->recommended_encoder_configuration); |
3619 | return feed->nb_streams - 1; |
3620 | } |
3621 | |
3622 | static void remove_stream(FFServerStream *stream) |
3623 | { |
3624 | FFServerStream **ps; |
3625 | ps = &config.first_stream; |
3626 | while (*ps) { |
3627 | if (*ps == stream) |
3628 | *ps = (*ps)->next; |
3629 | else |
3630 | ps = &(*ps)->next; |
3631 | } |
3632 | } |
3633 | |
3634 | /* compute the needed AVStream for each file */ |
3635 | static void build_file_streams(void) |
3636 | { |
3637 | FFServerStream *stream; |
3638 | AVFormatContext *infile; |
3639 | int i, ret; |
3640 | |
3641 | /* gather all streams */ |
3642 | for(stream = config.first_stream; stream; stream = stream->next) { |
3643 | infile = NULL; |
3644 | |
3645 | if (stream->stream_type != STREAM_TYPE_LIVE || stream->feed) |
3646 | continue; |
3647 | |
3648 | /* the stream comes from a file */ |
3649 | /* try to open the file */ |
3650 | /* open stream */ |
3651 | |
3652 | |
3653 | /* specific case: if transport stream output to RTP, |
3654 | * we use a raw transport stream reader */ |
3655 | if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) |
3656 | av_dict_set(&stream->in_opts, "mpeg2ts_compute_pcr", "1", 0); |
3657 | |
3658 | if (!stream->feed_filename[0]) { |
3659 | http_log("Unspecified feed file for stream '%s'\n", |
3660 | stream->filename); |
3661 | goto fail; |
3662 | } |
3663 | |
3664 | http_log("Opening feed file '%s' for stream '%s'\n", |
3665 | stream->feed_filename, stream->filename); |
3666 | |
3667 | ret = avformat_open_input(&infile, stream->feed_filename, |
3668 | stream->ifmt, &stream->in_opts); |
3669 | if (ret < 0) { |
3670 | http_log("Could not open '%s': %s\n", stream->feed_filename, |
3671 | av_err2str(ret)); |
3672 | /* remove stream (no need to spend more time on it) */ |
3673 | fail: |
3674 | remove_stream(stream); |
3675 | } else { |
3676 | /* find all the AVStreams inside and reference them in |
3677 | * 'stream' */ |
3678 | if (avformat_find_stream_info(infile, NULL) < 0) { |
3679 | http_log("Could not find codec parameters from '%s'\n", |
3680 | stream->feed_filename); |
3681 | avformat_close_input(&infile); |
3682 | goto fail; |
3683 | } |
3684 | |
3685 | for(i=0;i<infile->nb_streams;i++) |
3686 | add_av_stream1(stream, infile->streams[i]->codec, 1); |
3687 | |
3688 | avformat_close_input(&infile); |
3689 | } |
3690 | } |
3691 | } |
3692 | |
3693 | static inline |
3694 | int check_codec_match(LayeredAVStream *ccf, AVStream *ccs, int stream) |
3695 | { |
3696 | int matches = 1; |
3697 | |
3698 | /* FIXME: Missed check on AVCodecContext.flags */ |
3699 | #define CHECK_CODEC(x) (ccf->codecpar->x != ccs->codecpar->x) |
3700 | if (CHECK_CODEC(codec_id) || CHECK_CODEC(codec_type)) { |
3701 | http_log("Codecs do not match for stream %d\n", stream); |
3702 | matches = 0; |
3703 | } else if (CHECK_CODEC(bit_rate)) { |
3704 | http_log("Codec bitrates do not match for stream %d\n", stream); |
3705 | matches = 0; |
3706 | } else if (ccf->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { |
3707 | if (av_cmp_q(ccf->time_base, ccs->time_base) || |
3708 | CHECK_CODEC(width) || CHECK_CODEC(height)) { |
3709 | http_log("Codec width, height or framerate do not match for stream %d\n", stream); |
3710 | matches = 0; |
3711 | } |
3712 | } else if (ccf->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { |
3713 | if (CHECK_CODEC(sample_rate) || |
3714 | CHECK_CODEC(channels) || |
3715 | CHECK_CODEC(frame_size)) { |
3716 | http_log("Codec sample_rate, channels, frame_size do not match for stream %d\n", stream); |
3717 | matches = 0; |
3718 | } |
3719 | } else { |
3720 | http_log("Unknown codec type for stream %d\n", stream); |
3721 | matches = 0; |
3722 | } |
3723 | |
3724 | return matches; |
3725 | } |
3726 | |
3727 | /* compute the needed AVStream for each feed */ |
3728 | static int build_feed_streams(void) |
3729 | { |
3730 | FFServerStream *stream, *feed; |
3731 | int i, fd; |
3732 | |
3733 | /* gather all streams */ |
3734 | for(stream = config.first_stream; stream; stream = stream->next) { |
3735 | feed = stream->feed; |
3736 | if (!feed) |
3737 | continue; |
3738 | |
3739 | if (stream->is_feed) { |
3740 | for(i=0;i<stream->nb_streams;i++) |
3741 | stream->feed_streams[i] = i; |
3742 | continue; |
3743 | } |
3744 | /* we handle a stream coming from a feed */ |
3745 | for(i=0;i<stream->nb_streams;i++) |
3746 | stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]); |
3747 | } |
3748 | |
3749 | /* create feed files if needed */ |
3750 | for(feed = config.first_feed; feed; feed = feed->next_feed) { |
3751 | |
3752 | if (avio_check(feed->feed_filename, AVIO_FLAG_READ) > 0) { |
3753 | AVFormatContext *s = NULL; |
3754 | int matches = 0; |
3755 | |
3756 | /* See if it matches */ |
3757 | |
3758 | if (avformat_open_input(&s, feed->feed_filename, NULL, NULL) < 0) { |
3759 | http_log("Deleting feed file '%s' as it appears " |
3760 | "to be corrupt\n", |
3761 | feed->feed_filename); |
3762 | goto drop; |
3763 | } |
3764 | |
3765 | /* set buffer size */ |
3766 | if (ffio_set_buf_size(s->pb, FFM_PACKET_SIZE) < 0) { |
3767 | http_log("Failed to set buffer size\n"); |
3768 | avformat_close_input(&s); |
3769 | goto bail; |
3770 | } |
3771 | |
3772 | /* Now see if it matches */ |
3773 | if (s->nb_streams != feed->nb_streams) { |
3774 | http_log("Deleting feed file '%s' as stream counts " |
3775 | "differ (%d != %d)\n", |
3776 | feed->feed_filename, s->nb_streams, feed->nb_streams); |
3777 | goto drop; |
3778 | } |
3779 | |
3780 | matches = 1; |
3781 | for(i=0;i<s->nb_streams;i++) { |
3782 | AVStream *ss; |
3783 | LayeredAVStream *sf; |
3784 | |
3785 | sf = feed->streams[i]; |
3786 | ss = s->streams[i]; |
3787 | |
3788 | if (sf->index != ss->index || sf->id != ss->id) { |
3789 | http_log("Index & Id do not match for stream %d (%s)\n", |
3790 | i, feed->feed_filename); |
3791 | matches = 0; |
3792 | break; |
3793 | } |
3794 | |
3795 | matches = check_codec_match (sf, ss, i); |
3796 | if (!matches) |
3797 | break; |
3798 | } |
3799 | |
3800 | drop: |
3801 | if (s) |
3802 | avformat_close_input(&s); |
3803 | |
3804 | if (!matches) { |
3805 | if (feed->readonly) { |
3806 | http_log("Unable to delete read-only feed file '%s'\n", |
3807 | feed->feed_filename); |
3808 | goto bail; |
3809 | } |
3810 | unlink(feed->feed_filename); |
3811 | } |
3812 | } |
3813 | |
3814 | if (avio_check(feed->feed_filename, AVIO_FLAG_WRITE) <= 0) { |
3815 | AVFormatContext *s = avformat_alloc_context(); |
3816 | |
3817 | if (!s) { |
3818 | http_log("Failed to allocate context\n"); |
3819 | goto bail; |
3820 | } |
3821 | |
3822 | if (feed->readonly) { |
3823 | http_log("Unable to create feed file '%s' as it is " |
3824 | "marked readonly\n", |
3825 | feed->feed_filename); |
3826 | avformat_free_context(s); |
3827 | goto bail; |
3828 | } |
3829 | |
3830 | /* only write the header of the ffm file */ |
3831 | if (avio_open(&s->pb, feed->feed_filename, AVIO_FLAG_WRITE) < 0) { |
3832 | http_log("Could not open output feed file '%s'\n", |
3833 | feed->feed_filename); |
3834 | avformat_free_context(s); |
3835 | goto bail; |
3836 | } |
3837 | s->oformat = feed->fmt; |
3838 | for (i = 0; i<feed->nb_streams; i++) { |
3839 | AVStream *st = avformat_new_stream(s, NULL); // FIXME free this |
3840 | if (!st) { |
3841 | http_log("Failed to allocate stream\n"); |
3842 | goto bail; |
3843 | } |
3844 | unlayer_stream(st, feed->streams[i]); |
3845 | } |
3846 | if (avformat_write_header(s, NULL) < 0) { |
3847 | http_log("Container doesn't support the required parameters\n"); |
3848 | avio_closep(&s->pb); |
3849 | s->streams = NULL; |
3850 | s->nb_streams = 0; |
3851 | avformat_free_context(s); |
3852 | goto bail; |
3853 | } |
3854 | /* XXX: need better API */ |
3855 | av_freep(&s->priv_data); |
3856 | avio_closep(&s->pb); |
3857 | s->streams = NULL; |
3858 | s->nb_streams = 0; |
3859 | avformat_free_context(s); |
3860 | } |
3861 | |
3862 | /* get feed size and write index */ |
3863 | fd = open(feed->feed_filename, O_RDONLY); |
3864 | if (fd < 0) { |
3865 | http_log("Could not open output feed file '%s'\n", |
3866 | feed->feed_filename); |
3867 | goto bail; |
3868 | } |
3869 | |
3870 | feed->feed_write_index = FFMAX(ffm_read_write_index(fd), |
3871 | FFM_PACKET_SIZE); |
3872 | feed->feed_size = lseek(fd, 0, SEEK_END); |
3873 | /* ensure that we do not wrap before the end of file */ |
3874 | if (feed->feed_max_size && feed->feed_max_size < feed->feed_size) |
3875 | feed->feed_max_size = feed->feed_size; |
3876 | |
3877 | close(fd); |
3878 | } |
3879 | return 0; |
3880 | |
3881 | bail: |
3882 | return -1; |
3883 | } |
3884 | |
3885 | /* compute the bandwidth used by each stream */ |
3886 | static void compute_bandwidth(void) |
3887 | { |
3888 | unsigned bandwidth; |
3889 | int i; |
3890 | FFServerStream *stream; |
3891 | |
3892 | for(stream = config.first_stream; stream; stream = stream->next) { |
3893 | bandwidth = 0; |
3894 | for(i=0;i<stream->nb_streams;i++) { |
3895 | LayeredAVStream *st = stream->streams[i]; |
3896 | switch(st->codec->codec_type) { |
3897 | case AVMEDIA_TYPE_AUDIO: |
3898 | case AVMEDIA_TYPE_VIDEO: |
3899 | bandwidth += st->codec->bit_rate; |
3900 | break; |
3901 | default: |
3902 | break; |
3903 | } |
3904 | } |
3905 | stream->bandwidth = (bandwidth + 999) / 1000; |
3906 | } |
3907 | } |
3908 | |
3909 | static void handle_child_exit(int sig) |
3910 | { |
3911 | pid_t pid; |
3912 | int status; |
3913 | time_t uptime; |
3914 | |
3915 | while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { |
3916 | FFServerStream *feed; |
3917 | |
3918 | for (feed = config.first_feed; feed; feed = feed->next) { |
3919 | if (feed->pid != pid) |
3920 | continue; |
3921 | |
3922 | uptime = time(0) - feed->pid_start; |
3923 | feed->pid = 0; |
3924 | fprintf(stderr, |
3925 | "%s: Pid %"PRId64" exited with status %d after %"PRId64" " |
3926 | "seconds\n", |
3927 | feed->filename, (int64_t) pid, status, (int64_t)uptime); |
3928 | |
3929 | if (uptime < 30) |
3930 | /* Turn off any more restarts */ |
3931 | ffserver_free_child_args(&feed->child_argv); |
3932 | } |
3933 | } |
3934 | |
3935 | need_to_start_children = 1; |
3936 | } |
3937 | |
3938 | static void opt_debug(void) |
3939 | { |
3940 | config.debug = 1; |
3941 | snprintf(config.logfilename, sizeof(config.logfilename), "-"); |
3942 | } |
3943 | |
3944 | void show_help_default(const char *opt, const char *arg) |
3945 | { |
3946 | printf("usage: ffserver [options]\n" |
3947 | "Hyper fast multi format Audio/Video streaming server\n"); |
3948 | printf("\n"); |
3949 | show_help_options(options, "Main options:", 0, 0, 0); |
3950 | } |
3951 | |
3952 | static const OptionDef options[] = { |
3953 | #include "cmdutils_common_opts.h" |
3954 | { "n", OPT_BOOL, {(void *)&no_launch }, "enable no-launch mode" }, |
3955 | { "d", 0, {(void*)opt_debug}, "enable debug mode" }, |
3956 | { "f", HAS_ARG | OPT_STRING, {(void*)&config.filename }, "use configfile instead of /etc/ffserver.conf", "configfile" }, |
3957 | { NULL }, |
3958 | }; |
3959 | |
3960 | int main(int argc, char **argv) |
3961 | { |
3962 | struct sigaction sigact = { { 0 } }; |
3963 | int cfg_parsed; |
3964 | int ret = EXIT_FAILURE; |
3965 | |
3966 | init_dynload(); |
3967 | |
3968 | config.filename = av_strdup("/etc/ffserver.conf"); |
3969 | |
3970 | parse_loglevel(argc, argv, options); |
3971 | av_register_all(); |
3972 | avformat_network_init(); |
3973 | |
3974 | show_banner(argc, argv, options); |
3975 | |
3976 | my_program_name = argv[0]; |
3977 | |
3978 | parse_options(NULL, argc, argv, options, NULL); |
3979 | |
3980 | unsetenv("http_proxy"); /* Kill the http_proxy */ |
3981 | |
3982 | av_lfg_init(&random_state, av_get_random_seed()); |
3983 | |
3984 | sigact.sa_handler = handle_child_exit; |
3985 | sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART; |
3986 | sigaction(SIGCHLD, &sigact, 0); |
3987 | |
3988 | if ((cfg_parsed = ffserver_parse_ffconfig(config.filename, &config)) < 0) { |
3989 | fprintf(stderr, "Error reading configuration file '%s': %s\n", |
3990 | config.filename, av_err2str(cfg_parsed)); |
3991 | goto bail; |
3992 | } |
3993 | |
3994 | /* open log file if needed */ |
3995 | if (config.logfilename[0] != '\0') { |
3996 | if (!strcmp(config.logfilename, "-")) |
3997 | logfile = stdout; |
3998 | else |
3999 | logfile = fopen(config.logfilename, "a"); |
4000 | av_log_set_callback(http_av_log); |
4001 | } |
4002 | |
4003 | build_file_streams(); |
4004 | |
4005 | if (build_feed_streams() < 0) { |
4006 | http_log("Could not setup feed streams\n"); |
4007 | goto bail; |
4008 | } |
4009 | |
4010 | compute_bandwidth(); |
4011 | |
4012 | /* signal init */ |
4013 | signal(SIGPIPE, SIG_IGN); |
4014 | |
4015 | if (http_server() < 0) { |
4016 | http_log("Could not start server\n"); |
4017 | goto bail; |
4018 | } |
4019 | |
4020 | ret=EXIT_SUCCESS; |
4021 | |
4022 | bail: |
4023 | av_freep (&config.filename); |
4024 | avformat_network_deinit(); |
4025 | return ret; |
4026 | } |
4027 |