ffserver.c 155 KB
Newer Older
Fabrice Bellard's avatar
Fabrice Bellard committed
1
/*
2
 * Copyright (c) 2000, 2001, 2002 Fabrice Bellard
Fabrice Bellard's avatar
Fabrice Bellard committed
3
 *
4 5 6
 * This file is part of FFmpeg.
 *
 * FFmpeg is free software; you can redistribute it and/or
7 8
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
9
 * version 2.1 of the License, or (at your option) any later version.
Fabrice Bellard's avatar
Fabrice Bellard committed
10
 *
11
 * FFmpeg is distributed in the hope that it will be useful,
Fabrice Bellard's avatar
Fabrice Bellard committed
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
Fabrice Bellard's avatar
Fabrice Bellard committed
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
Fabrice Bellard's avatar
Fabrice Bellard committed
19
 */
20

21 22 23 24 25
/**
 * @file
 * multiple format streaming server based on the FFmpeg libraries
 */

26
#include "config.h"
27
#if !HAVE_CLOSESOCKET
28 29 30 31
#define closesocket close
#endif
#include <string.h>
#include <stdlib.h>
32
#include "libavformat/avformat.h"
33
// FIXME those are internal headers, avserver _really_ shouldn't use them
34
#include "libavformat/ffm.h"
35 36
#include "libavformat/network.h"
#include "libavformat/os_support.h"
37
#include "libavformat/rtpdec.h"
38
#include "libavformat/rtsp.h"
39
#include "libavformat/avio_internal.h"
40 41 42
#include "libavformat/internal.h"
#include "libavformat/url.h"

43
#include "libavutil/avstring.h"
44
#include "libavutil/lfg.h"
45
#include "libavutil/dict.h"
46
#include "libavutil/mathematics.h"
47
#include "libavutil/random_seed.h"
48
#include "libavutil/parseutils.h"
49
#include "libavutil/opt.h"
Fabrice Bellard's avatar
Fabrice Bellard committed
50 51 52 53
#include <stdarg.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
54
#if HAVE_POLL_H
55
#include <poll.h>
56
#endif
Fabrice Bellard's avatar
Fabrice Bellard committed
57 58 59
#include <errno.h>
#include <sys/time.h>
#include <time.h>
60
#include <sys/wait.h>
Fabrice Bellard's avatar
Fabrice Bellard committed
61
#include <signal.h>
62
#if HAVE_DLFCN_H
63
#include <dlfcn.h>
64
#endif
65

66
#include "cmdutils.h"
Fabrice Bellard's avatar
Fabrice Bellard committed
67

68
const char program_name[] = "ffserver";
69
const int program_birth_year = 2000;
70

71 72
static const OptionDef options[];

Fabrice Bellard's avatar
Fabrice Bellard committed
73 74 75 76
enum HTTPState {
    HTTPSTATE_WAIT_REQUEST,
    HTTPSTATE_SEND_HEADER,
    HTTPSTATE_SEND_DATA_HEADER,
77
    HTTPSTATE_SEND_DATA,          /* sending TCP or UDP data */
Fabrice Bellard's avatar
Fabrice Bellard committed
78
    HTTPSTATE_SEND_DATA_TRAILER,
79
    HTTPSTATE_RECEIVE_DATA,
80 81 82 83 84
    HTTPSTATE_WAIT_FEED,          /* wait for data from the feed */
    HTTPSTATE_READY,

    RTSPSTATE_WAIT_REQUEST,
    RTSPSTATE_SEND_REPLY,
85
    RTSPSTATE_SEND_PACKET,
Fabrice Bellard's avatar
Fabrice Bellard committed
86 87
};

Baptiste Coudurier's avatar
Baptiste Coudurier committed
88
static const char *http_state[] = {
89 90 91
    "HTTP_WAIT_REQUEST",
    "HTTP_SEND_HEADER",

Fabrice Bellard's avatar
Fabrice Bellard committed
92 93 94 95 96
    "SEND_DATA_HEADER",
    "SEND_DATA",
    "SEND_DATA_TRAILER",
    "RECEIVE_DATA",
    "WAIT_FEED",
97 98 99 100
    "READY",

    "RTSP_WAIT_REQUEST",
    "RTSP_SEND_REPLY",
101
    "RTSP_SEND_PACKET",
Fabrice Bellard's avatar
Fabrice Bellard committed
102 103
};

104 105
#define MAX_STREAMS 20

106
#define IOBUFFER_INIT_SIZE 8192
Fabrice Bellard's avatar
Fabrice Bellard committed
107 108

/* timeouts are in ms */
109 110 111
#define HTTP_REQUEST_TIMEOUT (15 * 1000)
#define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)

Fabrice Bellard's avatar
Fabrice Bellard committed
112 113
#define SYNC_TIMEOUT (10 * 1000)

114 115 116 117 118
typedef struct RTSPActionServerSetup {
    uint32_t ipaddr;
    char transport_option[512];
} RTSPActionServerSetup;

119
typedef struct {
120
    int64_t count1, count2;
121
    int64_t time1, time2;
122 123
} DataRateData;

Fabrice Bellard's avatar
Fabrice Bellard committed
124 125 126 127 128 129
/* context associated with one connection */
typedef struct HTTPContext {
    enum HTTPState state;
    int fd; /* socket file descriptor */
    struct sockaddr_in from_addr; /* origin */
    struct pollfd *poll_entry; /* used when polling */
130
    int64_t timeout;
131
    uint8_t *buffer_ptr, *buffer_end;
Fabrice Bellard's avatar
Fabrice Bellard committed
132
    int http_error;
133
    int post;
134 135
    int chunked_encoding;
    int chunk_size;               /* 0 if it needs to be read */
Fabrice Bellard's avatar
Fabrice Bellard committed
136
    struct HTTPContext *next;
137
    int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
138
    int64_t data_count;
Fabrice Bellard's avatar
Fabrice Bellard committed
139 140 141 142
    /* feed input */
    int feed_fd;
    /* input format handling */
    AVFormatContext *fmt_in;
143
    int64_t start_time;            /* In milliseconds - this wraps fairly often */
144
    int64_t first_pts;            /* initial pts value */
145 146 147 148 149 150 151
    int64_t cur_pts;             /* current pts value from the stream in us */
    int64_t cur_frame_duration;  /* duration of the current frame in us */
    int cur_frame_bytes;       /* output frame size, needed to compute
                                  the time at which we send each
                                  packet */
    int pts_stream_index;        /* stream we choose as clock reference */
    int64_t cur_clock;           /* current clock reference value in us */
Fabrice Bellard's avatar
Fabrice Bellard committed
152 153
    /* output format handling */
    struct FFStream *stream;
154 155 156 157
    /* -1 is invalid stream */
    int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
    int switch_feed_streams[MAX_STREAMS]; /* index of streams in the feed */
    int switch_pending;
158
    AVFormatContext fmt_ctx; /* instance of FFStream for one user */
Fabrice Bellard's avatar
Fabrice Bellard committed
159
    int last_packet_sent; /* true if last data packet was sent */
160
    int suppress_log;
161
    DataRateData datarate;
162
    int wmp_client_id;
163 164 165
    char protocol[16];
    char method[16];
    char url[128];
166
    int buffer_size;
167
    uint8_t *buffer;
168 169
    int is_packetized; /* if true, the stream is packetized */
    int packet_stream_index; /* current stream for output in state machine */
170

171
    /* RTSP state specific */
172
    uint8_t *pb_buffer; /* XXX: use that in all the code */
173
    AVIOContext *pb;
174
    int seq; /* RTSP sequence number */
175

176
    /* RTP state specific */
177
    enum RTSPLowerTransport rtp_protocol;
178 179
    char session_id[32]; /* session id */
    AVFormatContext *rtp_ctx[MAX_STREAMS];
180

181 182 183 184 185 186
    /* RTP/UDP specific */
    URLContext *rtp_handles[MAX_STREAMS];

    /* RTP/TCP specific */
    struct HTTPContext *rtsp_c;
    uint8_t *packet_buffer, *packet_buffer_ptr, *packet_buffer_end;
Fabrice Bellard's avatar
Fabrice Bellard committed
187 188 189 190 191 192
} HTTPContext;

/* each generated stream is described here */
enum StreamType {
    STREAM_TYPE_LIVE,
    STREAM_TYPE_STATUS,
193
    STREAM_TYPE_REDIRECT,
Fabrice Bellard's avatar
Fabrice Bellard committed
194 195
};

196 197 198 199 200 201 202 203
enum IPAddressAction {
    IP_ALLOW = 1,
    IP_DENY,
};

typedef struct IPAddressACL {
    struct IPAddressACL *next;
    enum IPAddressAction action;
204
    /* These are in host order */
205 206 207 208
    struct in_addr first;
    struct in_addr last;
} IPAddressACL;

Fabrice Bellard's avatar
Fabrice Bellard committed
209 210 211 212
/* description of each stream of the ffserver.conf file */
typedef struct FFStream {
    enum StreamType stream_type;
    char filename[1024];     /* stream filename */
213 214
    struct FFStream *feed;   /* feed we are using (can be null if
                                coming from file) */
215
    AVDictionary *in_opts;   /* input parameters */
216
    AVInputFormat *ifmt;       /* if non NULL, force input format */
217
    AVOutputFormat *fmt;
218
    IPAddressACL *acl;
219
    char dynamic_acl[1024];
Fabrice Bellard's avatar
Fabrice Bellard committed
220
    int nb_streams;
221
    int prebuffer;      /* Number of millseconds early to start */
222
    int64_t max_time;      /* Number of milliseconds to run */
223
    int send_on_key;
Fabrice Bellard's avatar
Fabrice Bellard committed
224 225 226 227
    AVStream *streams[MAX_STREAMS];
    int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
    char feed_filename[1024]; /* file name of the feed storage, or
                                 input file name for a stream */
228 229 230 231
    char author[512];
    char title[512];
    char copyright[512];
    char comment[512];
232
    pid_t pid;  /* Of ffmpeg process */
233
    time_t pid_start;  /* Of ffmpeg process */
234
    char **child_argv;
Fabrice Bellard's avatar
Fabrice Bellard committed
235
    struct FFStream *next;
236
    unsigned bandwidth; /* bandwidth, in kbits/s */
237 238
    /* RTSP options */
    char *rtsp_option;
239 240 241 242
    /* multicast specific */
    int is_multicast;
    struct in_addr multicast_ip;
    int multicast_port; /* first port used for multicast */
243 244
    int multicast_ttl;
    int loop; /* if true, send the stream in loops (only meaningful if file) */
245

Fabrice Bellard's avatar
Fabrice Bellard committed
246
    /* feed specific */
247
    int feed_opened;     /* true if someone is writing to the feed */
Fabrice Bellard's avatar
Fabrice Bellard committed
248
    int is_feed;         /* true if it is a feed */
249
    int readonly;        /* True if writing is prohibited to the file */
250
    int truncate;        /* True if feeder connection truncate the feed file */
251
    int conns_served;
252
    int64_t bytes_served;
253
    int64_t feed_max_size;      /* maximum storage size, zero means unlimited */
Diego Biurrun's avatar
Diego Biurrun committed
254
    int64_t feed_write_index;   /* current write position in feed (it wraps around) */
255
    int64_t feed_size;          /* current size of feed */
Fabrice Bellard's avatar
Fabrice Bellard committed
256 257 258 259 260
    struct FFStream *next_feed;
} FFStream;

typedef struct FeedData {
    long long data_count;
Diego Biurrun's avatar
Diego Biurrun committed
261
    float avg_frame_size;   /* frame size averaged over last frames with exponential mean */
Fabrice Bellard's avatar
Fabrice Bellard committed
262 263
} FeedData;

264 265
static struct sockaddr_in my_http_addr;
static struct sockaddr_in my_rtsp_addr;
266

267 268 269 270
static char logfilename[1024];
static HTTPContext *first_http_ctx;
static FFStream *first_feed;   /* contains only feeds */
static FFStream *first_stream; /* contains all streams, including feeds */
Fabrice Bellard's avatar
Fabrice Bellard committed
271

272 273 274 275 276
static void new_connection(int server_fd, int is_rtsp);
static void close_connection(HTTPContext *c);

/* HTTP handling */
static int handle_connection(HTTPContext *c);
Fabrice Bellard's avatar
Fabrice Bellard committed
277
static int http_parse_request(HTTPContext *c);
278
static int http_send_data(HTTPContext *c);
279
static void compute_status(HTTPContext *c);
Fabrice Bellard's avatar
Fabrice Bellard committed
280 281 282
static int open_input_stream(HTTPContext *c, const char *info);
static int http_start_receive_data(HTTPContext *c);
static int http_receive_data(HTTPContext *c);
283 284 285 286

/* RTSP handling */
static int rtsp_parse_request(HTTPContext *c);
static void rtsp_cmd_describe(HTTPContext *c, const char *url);
287
static void rtsp_cmd_options(HTTPContext *c, const char *url);
288 289 290 291
static void rtsp_cmd_setup(HTTPContext *c, const char *url, RTSPMessageHeader *h);
static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPMessageHeader *h);
static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPMessageHeader *h);
static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPMessageHeader *h);
292

293
/* SDP handling */
294
static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
295 296
                                   struct in_addr my_ip);

297
/* RTP handling */
298
static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
299
                                       FFStream *stream, const char *session_id,
300
                                       enum RTSPLowerTransport rtp_protocol);
301
static int rtp_new_av_stream(HTTPContext *c,
302 303
                             int stream_index, struct sockaddr_in *dest_addr,
                             HTTPContext *rtsp_c);
Fabrice Bellard's avatar
Fabrice Bellard committed
304

305
static const char *my_program_name;
306
static const char *my_program_dir;
307

308 309
static const char *config_filename = "/etc/ffserver.conf";

310
static int ffserver_debug;
311
static int ffserver_daemon;
312
static int no_launch;
313
static int need_to_start_children;
314

315 316
/* maximum number of simultaneous HTTP connections */
static unsigned int nb_max_http_connections = 2000;
317 318
static unsigned int nb_max_connections = 5;
static unsigned int nb_connections;
Fabrice Bellard's avatar
Fabrice Bellard committed
319

320
static uint64_t max_bandwidth = 1000;
321
static uint64_t current_bandwidth;
322

323
static int64_t cur_time;           // Making this global saves on passing it around everywhere
324

325
static AVLFG random_state;
326

Fabrice Bellard's avatar
Fabrice Bellard committed
327 328
static FILE *logfile = NULL;

329
/* FIXME: make ffserver work with IPv6 */
330
void av_noreturn exit_program(int ret)
331 332 333 334
{
    exit(ret);
}

335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369
/* resolve host with also IP address parsing */
static int resolve_host(struct in_addr *sin_addr, const char *hostname)
{

    if (!ff_inet_aton(hostname, sin_addr)) {
#if HAVE_GETADDRINFO
        struct addrinfo *ai, *cur;
        struct addrinfo hints;
        memset(&hints, 0, sizeof(hints));
        hints.ai_family = AF_INET;
        if (getaddrinfo(hostname, NULL, &hints, &ai))
            return -1;
        /* getaddrinfo returns a linked list of addrinfo structs.
         * Even if we set ai_family = AF_INET above, make sure
         * that the returned one actually is of the correct type. */
        for (cur = ai; cur; cur = cur->ai_next) {
            if (cur->ai_family == AF_INET) {
                *sin_addr = ((struct sockaddr_in *)cur->ai_addr)->sin_addr;
                freeaddrinfo(ai);
                return 0;
            }
        }
        freeaddrinfo(ai);
        return -1;
#else
        struct hostent *hp;
        hp = gethostbyname(hostname);
        if (!hp)
            return -1;
        memcpy(sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
#endif
    }
    return 0;
}

Baptiste Coudurier's avatar
Baptiste Coudurier committed
370 371 372 373 374 375 376 377 378 379 380 381 382 383
static char *ctime1(char *buf2)
{
    time_t ti;
    char *p;

    ti = time(NULL);
    p = ctime(&ti);
    strcpy(buf2, p);
    p = buf2 + strlen(p) - 1;
    if (*p == '\n')
        *p = '\0';
    return buf2;
}

384
static void http_vlog(const char *fmt, va_list vargs)
Fabrice Bellard's avatar
Fabrice Bellard committed
385
{
386
    static int print_prefix = 1;
387
    if (logfile) {
388
        if (print_prefix) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
389 390 391
            char buf[32];
            ctime1(buf);
            fprintf(logfile, "%s ", buf);
392 393
        }
        print_prefix = strstr(fmt, "\n") != NULL;
394
        vfprintf(logfile, fmt, vargs);
395 396
        fflush(logfile);
    }
397 398
}

399 400 401 402
#ifdef __GNUC__
__attribute__ ((format (printf, 1, 2)))
#endif
static void http_log(const char *fmt, ...)
403 404 405 406 407 408 409 410 411 412 413
{
    va_list vargs;
    va_start(vargs, fmt);
    http_vlog(fmt, vargs);
    va_end(vargs);
}

static void http_av_log(void *ptr, int level, const char *fmt, va_list vargs)
{
    static int print_prefix = 1;
    AVClass *avc = ptr ? *(AVClass**)ptr : NULL;
414
    if (level > av_log_get_level())
415 416
        return;
    if (print_prefix && avc)
417
        http_log("[%s @ %p]", avc->item_name(ptr), ptr);
418 419
    print_prefix = strstr(fmt, "\n") != NULL;
    http_vlog(fmt, vargs);
Fabrice Bellard's avatar
Fabrice Bellard committed
420 421
}

422 423
static void log_connection(HTTPContext *c)
{
424
    if (c->suppress_log)
425 426
        return;

427 428
    http_log("%s - - [%s] \"%s %s\" %d %"PRId64"\n",
             inet_ntoa(c->from_addr.sin_addr), c->method, c->url,
429
             c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
430 431
}

432
static void update_datarate(DataRateData *drd, int64_t count)
433 434 435 436
{
    if (!drd->time1 && !drd->count1) {
        drd->time1 = drd->time2 = cur_time;
        drd->count1 = drd->count2 = count;
437
    } else if (cur_time - drd->time2 > 5000) {
Alex Beregszaszi's avatar
Alex Beregszaszi committed
438 439 440 441
        drd->time1 = drd->time2;
        drd->count1 = drd->count2;
        drd->time2 = cur_time;
        drd->count2 = count;
442 443 444 445
    }
}

/* In bytes per second */
446
static int compute_datarate(DataRateData *drd, int64_t count)
447 448 449
{
    if (cur_time == drd->time1)
        return 0;
450

451 452 453
    return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
}

454

455 456
static void start_children(FFStream *feed)
{
457 458 459
    if (no_launch)
        return;

460
    for (; feed; feed = feed->next) {
461 462 463
        if (feed->child_argv && !feed->pid) {
            feed->pid_start = time(0);

464 465 466
            feed->pid = fork();

            if (feed->pid < 0) {
467
                http_log("Unable to create children\n");
468 469 470 471 472 473 474 475
                exit(1);
            }
            if (!feed->pid) {
                /* In child */
                char pathname[1024];
                char *slash;
                int i;

476 477 478 479 480 481 482 483 484
                av_strlcpy(pathname, my_program_name, sizeof(pathname));

                slash = strrchr(pathname, '/');
                if (!slash)
                    slash = pathname;
                else
                    slash++;
                strcpy(slash, "ffmpeg");

485
                http_log("Launch command line: ");
Stefano Sabatini's avatar
Stefano Sabatini committed
486 487 488 489
                http_log("%s ", pathname);
                for (i = 1; feed->child_argv[i] && feed->child_argv[i][0]; i++)
                    http_log("%s ", feed->child_argv[i]);
                http_log("\n");
490

491
                for (i = 3; i < 256; i++)
492
                    close(i);
493

494
                if (!ffserver_debug) {
495
                    i = open("/dev/null", O_RDWR);
496
                    if (i != -1) {
497
                        dup2(i, 0);
498 499
                        dup2(i, 1);
                        dup2(i, 2);
500
                        close(i);
501
                    }
502
                }
503

504
                /* This is needed to make relative pathnames work */
505 506 507 508
                if (chdir(my_program_dir) < 0) {
                    http_log("chdir failed\n");
                    exit(1);
                }
509

510 511
                signal(SIGPIPE, SIG_DFL);

512 513 514 515 516 517
                execvp(pathname, feed->child_argv);

                _exit(1);
            }
        }
    }
518 519
}

520 521
/* open a listening socket */
static int socket_open_listen(struct sockaddr_in *my_addr)
Fabrice Bellard's avatar
Fabrice Bellard committed
522
{
523
    int server_fd, tmp;
Fabrice Bellard's avatar
Fabrice Bellard committed
524 525 526 527 528 529

    server_fd = socket(AF_INET,SOCK_STREAM,0);
    if (server_fd < 0) {
        perror ("socket");
        return -1;
    }
530

Fabrice Bellard's avatar
Fabrice Bellard committed
531 532 533
    tmp = 1;
    setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));

534
    my_addr->sin_family = AF_INET;
535
    if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
536 537 538
        char bindmsg[32];
        snprintf(bindmsg, sizeof(bindmsg), "bind(port %d)", ntohs(my_addr->sin_port));
        perror (bindmsg);
539
        closesocket(server_fd);
Fabrice Bellard's avatar
Fabrice Bellard committed
540 541
        return -1;
    }
542

Fabrice Bellard's avatar
Fabrice Bellard committed
543 544
    if (listen (server_fd, 5) < 0) {
        perror ("listen");
545
        closesocket(server_fd);
Fabrice Bellard's avatar
Fabrice Bellard committed
546 547
        return -1;
    }
548
    ff_socket_nonblock(server_fd, 1);
549 550 551 552

    return server_fd;
}

553 554 555 556 557 558 559 560 561 562 563 564 565
/* start all multicast streams */
static void start_multicast(void)
{
    FFStream *stream;
    char session_id[32];
    HTTPContext *rtp_c;
    struct sockaddr_in dest_addr;
    int default_port, stream_index;

    default_port = 6000;
    for(stream = first_stream; stream != NULL; stream = stream->next) {
        if (stream->is_multicast) {
            /* open the RTP connection */
566
            snprintf(session_id, sizeof(session_id), "%08x%08x",
567
                     av_lfg_get(&random_state), av_lfg_get(&random_state));
568 569 570 571 572 573 574 575 576 577 578

            /* choose a port if none given */
            if (stream->multicast_port == 0) {
                stream->multicast_port = default_port;
                default_port += 100;
            }

            dest_addr.sin_family = AF_INET;
            dest_addr.sin_addr = stream->multicast_ip;
            dest_addr.sin_port = htons(stream->multicast_port);

579
            rtp_c = rtp_new_connection(&dest_addr, stream, session_id,
580
                                       RTSP_LOWER_TRANSPORT_UDP_MULTICAST);
581
            if (!rtp_c)
582
                continue;
583

584
            if (open_input_stream(rtp_c, "") < 0) {
585 586
                http_log("Could not open input stream for stream '%s'\n",
                         stream->filename);
587 588 589 590
                continue;
            }

            /* open each RTP stream */
591
            for(stream_index = 0; stream_index < stream->nb_streams;
592
                stream_index++) {
593
                dest_addr.sin_port = htons(stream->multicast_port +
594
                                           2 * stream_index);
595
                if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, NULL) < 0) {
596 597
                    http_log("Could not open output stream '%s/streamid=%d'\n",
                             stream->filename, stream_index);
598
                    exit(1);
599 600 601 602 603 604 605 606
                }
            }

            /* change state to send data */
            rtp_c->state = HTTPSTATE_SEND_DATA;
        }
    }
}
607 608 609 610

/* main loop of the http server */
static int http_server(void)
{
611 612
    int server_fd = 0, rtsp_server_fd = 0;
    int ret, delay, delay1;
613
    struct pollfd *poll_table, *poll_entry;
614 615
    HTTPContext *c, *c_next;

616
    if(!(poll_table = av_mallocz((nb_max_http_connections + 2)*sizeof(*poll_table)))) {
617 618 619 620
        http_log("Impossible to allocate a poll table handling %d connections.\n", nb_max_http_connections);
        return -1;
    }

621
    if (my_http_addr.sin_port) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
622 623 624
        server_fd = socket_open_listen(&my_http_addr);
        if (server_fd < 0)
            return -1;
625
    }
Fabrice Bellard's avatar
Fabrice Bellard committed
626

627
    if (my_rtsp_addr.sin_port) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
628 629 630
        rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
        if (rtsp_server_fd < 0)
            return -1;
631 632 633 634 635 636
    }

    if (!rtsp_server_fd && !server_fd) {
        http_log("HTTP and RTSP disabled.\n");
        return -1;
    }
637

638
    http_log("FFserver started.\n");
Fabrice Bellard's avatar
Fabrice Bellard committed
639

640 641
    start_children(first_feed);

642 643
    start_multicast();

Fabrice Bellard's avatar
Fabrice Bellard committed
644 645
    for(;;) {
        poll_entry = poll_table;
646
        if (server_fd) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
647 648 649
            poll_entry->fd = server_fd;
            poll_entry->events = POLLIN;
            poll_entry++;
650 651
        }
        if (rtsp_server_fd) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
652 653 654
            poll_entry->fd = rtsp_server_fd;
            poll_entry->events = POLLIN;
            poll_entry++;
655
        }
656

Fabrice Bellard's avatar
Fabrice Bellard committed
657 658
        /* wait for events on each HTTP handle */
        c = first_http_ctx;
659
        delay = 1000;
Fabrice Bellard's avatar
Fabrice Bellard committed
660 661 662 663
        while (c != NULL) {
            int fd;
            fd = c->fd;
            switch(c->state) {
664 665
            case HTTPSTATE_SEND_HEADER:
            case RTSPSTATE_SEND_REPLY:
666
            case RTSPSTATE_SEND_PACKET:
Fabrice Bellard's avatar
Fabrice Bellard committed
667 668
                c->poll_entry = poll_entry;
                poll_entry->fd = fd;
669
                poll_entry->events = POLLOUT;
Fabrice Bellard's avatar
Fabrice Bellard committed
670 671 672 673 674
                poll_entry++;
                break;
            case HTTPSTATE_SEND_DATA_HEADER:
            case HTTPSTATE_SEND_DATA:
            case HTTPSTATE_SEND_DATA_TRAILER:
675 676 677 678 679 680 681
                if (!c->is_packetized) {
                    /* for TCP, we output as much as we can (may need to put a limit) */
                    c->poll_entry = poll_entry;
                    poll_entry->fd = fd;
                    poll_entry->events = POLLOUT;
                    poll_entry++;
                } else {
682 683 684 685 686 687
                    /* when ffserver is doing the timing, we work by
                       looking at which packet need to be sent every
                       10 ms */
                    delay1 = 10; /* one tick wait XXX: 10 ms assumed */
                    if (delay1 < delay)
                        delay = delay1;
688
                }
Fabrice Bellard's avatar
Fabrice Bellard committed
689
                break;
690
            case HTTPSTATE_WAIT_REQUEST:
Fabrice Bellard's avatar
Fabrice Bellard committed
691 692
            case HTTPSTATE_RECEIVE_DATA:
            case HTTPSTATE_WAIT_FEED:
693
            case RTSPSTATE_WAIT_REQUEST:
Fabrice Bellard's avatar
Fabrice Bellard committed
694 695 696
                /* need to catch errors */
                c->poll_entry = poll_entry;
                poll_entry->fd = fd;
697
                poll_entry->events = POLLIN;/* Maybe this will work */
Fabrice Bellard's avatar
Fabrice Bellard committed
698 699 700 701 702 703 704 705 706 707 708 709
                poll_entry++;
                break;
            default:
                c->poll_entry = NULL;
                break;
            }
            c = c->next;
        }

        /* wait for an event on one connection. We poll at least every
           second to handle timeouts */
        do {
710
            ret = poll(poll_table, poll_entry - poll_table, delay);
711 712
            if (ret < 0 && ff_neterrno() != AVERROR(EAGAIN) &&
                ff_neterrno() != AVERROR(EINTR))
713
                return -1;
714
        } while (ret < 0);
715

716
        cur_time = av_gettime() / 1000;
Fabrice Bellard's avatar
Fabrice Bellard committed
717

718 719 720 721 722
        if (need_to_start_children) {
            need_to_start_children = 0;
            start_children(first_feed);
        }

Fabrice Bellard's avatar
Fabrice Bellard committed
723
        /* now handle the events */
724 725 726
        for(c = first_http_ctx; c != NULL; c = c_next) {
            c_next = c->next;
            if (handle_connection(c) < 0) {
Fabrice Bellard's avatar
Fabrice Bellard committed
727
                /* close and free the connection */
728
                log_connection(c);
729
                close_connection(c);
Fabrice Bellard's avatar
Fabrice Bellard committed
730 731 732 733
            }
        }

        poll_entry = poll_table;
734
        if (server_fd) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
735 736 737 738
            /* new HTTP connection request ? */
            if (poll_entry->revents & POLLIN)
                new_connection(server_fd, 0);
            poll_entry++;
739 740
        }
        if (rtsp_server_fd) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
741 742 743
            /* new RTSP connection request ? */
            if (poll_entry->revents & POLLIN)
                new_connection(rtsp_server_fd, 1);
744
        }
Fabrice Bellard's avatar
Fabrice Bellard committed
745 746 747
    }
}

748 749
/* start waiting for a new HTTP/RTSP request */
static void start_wait_request(HTTPContext *c, int is_rtsp)
Fabrice Bellard's avatar
Fabrice Bellard committed
750
{
751 752 753 754 755 756 757 758 759 760 761 762
    c->buffer_ptr = c->buffer;
    c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */

    if (is_rtsp) {
        c->timeout = cur_time + RTSP_REQUEST_TIMEOUT;
        c->state = RTSPSTATE_WAIT_REQUEST;
    } else {
        c->timeout = cur_time + HTTP_REQUEST_TIMEOUT;
        c->state = HTTPSTATE_WAIT_REQUEST;
    }
}

763 764 765 766
static void http_send_too_busy_reply(int fd)
{
    char buffer[300];
    int len = snprintf(buffer, sizeof(buffer),
767
                       "HTTP/1.0 503 Server too busy\r\n"
768 769 770 771 772 773 774 775 776 777 778
                       "Content-type: text/html\r\n"
                       "\r\n"
                       "<html><head><title>Too busy</title></head><body>\r\n"
                       "<p>The server is too busy to serve your request at this time.</p>\r\n"
                       "<p>The number of current connections is %d, and this exceeds the limit of %d.</p>\r\n"
                       "</body></html>\r\n",
                       nb_connections, nb_max_connections);
    send(fd, buffer, len, 0);
}


779 780 781 782 783 784 785
static void new_connection(int server_fd, int is_rtsp)
{
    struct sockaddr_in from_addr;
    int fd, len;
    HTTPContext *c = NULL;

    len = sizeof(from_addr);
786
    fd = accept(server_fd, (struct sockaddr *)&from_addr,
787
                &len);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
788 789
    if (fd < 0) {
        http_log("error during accept %s\n", strerror(errno));
790
        return;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
791
    }
792
    ff_socket_nonblock(fd, 1);
793

794 795
    if (nb_connections >= nb_max_connections) {
        http_send_too_busy_reply(fd);
796
        goto fail;
797
    }
798

799 800 801 802
    /* add a new connection */
    c = av_mallocz(sizeof(HTTPContext));
    if (!c)
        goto fail;
803

804 805 806 807 808 809 810
    c->fd = fd;
    c->poll_entry = NULL;
    c->from_addr = from_addr;
    c->buffer_size = IOBUFFER_INIT_SIZE;
    c->buffer = av_malloc(c->buffer_size);
    if (!c->buffer)
        goto fail;
811 812 813

    c->next = first_http_ctx;
    first_http_ctx = c;
814
    nb_connections++;
815

816 817 818 819 820 821 822 823 824
    start_wait_request(c, is_rtsp);

    return;

 fail:
    if (c) {
        av_free(c->buffer);
        av_free(c);
    }
825
    closesocket(fd);
826 827 828 829 830 831 832 833 834 835 836 837 838 839
}

static void close_connection(HTTPContext *c)
{
    HTTPContext **cp, *c1;
    int i, nb_streams;
    AVFormatContext *ctx;
    URLContext *h;
    AVStream *st;

    /* remove connection from list */
    cp = &first_http_ctx;
    while ((*cp) != NULL) {
        c1 = *cp;
840
        if (c1 == c)
841
            *cp = c->next;
842
        else
843 844 845
            cp = &c1->next;
    }

846 847 848 849 850 851
    /* remove references, if any (XXX: do it faster) */
    for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
        if (c1->rtsp_c == c)
            c1->rtsp_c = NULL;
    }

852 853
    /* remove connection associated resources */
    if (c->fd >= 0)
854
        closesocket(c->fd);
855 856 857 858
    if (c->fmt_in) {
        /* close each frame parser */
        for(i=0;i<c->fmt_in->nb_streams;i++) {
            st = c->fmt_in->streams[i];
859
            if (st->codec->codec)
860
                avcodec_close(st->codec);
861
        }
862
        avformat_close_input(&c->fmt_in);
863 864 865 866
    }

    /* free RTP output streams if any */
    nb_streams = 0;
867
    if (c->stream)
868
        nb_streams = c->stream->nb_streams;
869

870 871 872 873
    for(i=0;i<nb_streams;i++) {
        ctx = c->rtp_ctx[i];
        if (ctx) {
            av_write_trailer(ctx);
874
            av_dict_free(&ctx->metadata);
875
            av_free(ctx->streams[0]);
876 877 878
            av_free(ctx);
        }
        h = c->rtp_handles[i];
879
        if (h)
880
            ffurl_close(h);
881
    }
882

883 884
    ctx = &c->fmt_ctx;

885
    if (!c->last_packet_sent && c->state == HTTPSTATE_SEND_DATA_TRAILER) {
886 887
        if (ctx->oformat) {
            /* prepare header */
888
            if (avio_open_dyn_buf(&ctx->pb) >= 0) {
889
                av_write_trailer(ctx);
890
                av_freep(&c->pb_buffer);
891
                avio_close_dyn_buf(ctx->pb, &c->pb_buffer);
892 893 894 895
            }
        }
    }

896
    for(i=0; i<ctx->nb_streams; i++)
Alex Beregszaszi's avatar
Alex Beregszaszi committed
897
        av_free(ctx->streams[i]);
898

899
    if (c->stream && !c->post && c->stream->stream_type == STREAM_TYPE_LIVE)
900
        current_bandwidth -= c->stream->bandwidth;
901 902 903 904 905 906 907

    /* signal that there is no feed if we are the feeder socket */
    if (c->state == HTTPSTATE_RECEIVE_DATA && c->stream) {
        c->stream->feed_opened = 0;
        close(c->feed_fd);
    }

908
    av_freep(&c->pb_buffer);
909
    av_freep(&c->packet_buffer);
910 911 912 913 914 915 916 917
    av_free(c->buffer);
    av_free(c);
    nb_connections--;
}

static int handle_connection(HTTPContext *c)
{
    int len, ret;
918

Fabrice Bellard's avatar
Fabrice Bellard committed
919 920
    switch(c->state) {
    case HTTPSTATE_WAIT_REQUEST:
921
    case RTSPSTATE_WAIT_REQUEST:
Fabrice Bellard's avatar
Fabrice Bellard committed
922 923 924 925 926 927 928 929 930 931
        /* timeout ? */
        if ((c->timeout - cur_time) < 0)
            return -1;
        if (c->poll_entry->revents & (POLLERR | POLLHUP))
            return -1;

        /* no need to read if no events */
        if (!(c->poll_entry->revents & POLLIN))
            return 0;
        /* read the data */
932
    read_loop:
933
        len = recv(c->fd, c->buffer_ptr, 1, 0);
Fabrice Bellard's avatar
Fabrice Bellard committed
934
        if (len < 0) {
935 936
            if (ff_neterrno() != AVERROR(EAGAIN) &&
                ff_neterrno() != AVERROR(EINTR))
Fabrice Bellard's avatar
Fabrice Bellard committed
937 938 939 940
                return -1;
        } else if (len == 0) {
            return -1;
        } else {
941
            /* search for end of request. */
942
            uint8_t *ptr;
Fabrice Bellard's avatar
Fabrice Bellard committed
943 944 945 946 947
            c->buffer_ptr += len;
            ptr = c->buffer_ptr;
            if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
                (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
                /* request found : parse it and reply */
948 949 950 951 952 953
                if (c->state == HTTPSTATE_WAIT_REQUEST) {
                    ret = http_parse_request(c);
                } else {
                    ret = rtsp_parse_request(c);
                }
                if (ret < 0)
Fabrice Bellard's avatar
Fabrice Bellard committed
954 955 956 957
                    return -1;
            } else if (ptr >= c->buffer_end) {
                /* request too long: cannot do anything */
                return -1;
958
            } else goto read_loop;
Fabrice Bellard's avatar
Fabrice Bellard committed
959 960 961 962 963 964 965
        }
        break;

    case HTTPSTATE_SEND_HEADER:
        if (c->poll_entry->revents & (POLLERR | POLLHUP))
            return -1;

966
        /* no need to write if no events */
Fabrice Bellard's avatar
Fabrice Bellard committed
967 968
        if (!(c->poll_entry->revents & POLLOUT))
            return 0;
969
        len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
Fabrice Bellard's avatar
Fabrice Bellard committed
970
        if (len < 0) {
971 972
            if (ff_neterrno() != AVERROR(EAGAIN) &&
                ff_neterrno() != AVERROR(EINTR)) {
Fabrice Bellard's avatar
Fabrice Bellard committed
973
                /* error : close connection */
974
                av_freep(&c->pb_buffer);
Fabrice Bellard's avatar
Fabrice Bellard committed
975 976 977 978
                return -1;
            }
        } else {
            c->buffer_ptr += len;
979 980
            if (c->stream)
                c->stream->bytes_served += len;
981
            c->data_count += len;
Fabrice Bellard's avatar
Fabrice Bellard committed
982
            if (c->buffer_ptr >= c->buffer_end) {
983
                av_freep(&c->pb_buffer);
Fabrice Bellard's avatar
Fabrice Bellard committed
984
                /* if error, exit */
985
                if (c->http_error)
Fabrice Bellard's avatar
Fabrice Bellard committed
986
                    return -1;
987
                /* all the buffer was sent : synchronize to the incoming stream */
Fabrice Bellard's avatar
Fabrice Bellard committed
988 989 990 991 992 993 994 995 996
                c->state = HTTPSTATE_SEND_DATA_HEADER;
                c->buffer_ptr = c->buffer_end = c->buffer;
            }
        }
        break;

    case HTTPSTATE_SEND_DATA:
    case HTTPSTATE_SEND_DATA_HEADER:
    case HTTPSTATE_SEND_DATA_TRAILER:
997 998 999 1000 1001 1002
        /* for packetized output, we consider we can always write (the
           input streams sets the speed). It may be better to verify
           that we do not rely too much on the kernel queues */
        if (!c->is_packetized) {
            if (c->poll_entry->revents & (POLLERR | POLLHUP))
                return -1;
1003

1004 1005 1006 1007
            /* no need to read if no events */
            if (!(c->poll_entry->revents & POLLOUT))
                return 0;
        }
1008
        if (http_send_data(c) < 0)
Fabrice Bellard's avatar
Fabrice Bellard committed
1009
            return -1;
1010 1011 1012
        /* close connection if trailer sent */
        if (c->state == HTTPSTATE_SEND_DATA_TRAILER)
            return -1;
Fabrice Bellard's avatar
Fabrice Bellard committed
1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024
        break;
    case HTTPSTATE_RECEIVE_DATA:
        /* no need to read if no events */
        if (c->poll_entry->revents & (POLLERR | POLLHUP))
            return -1;
        if (!(c->poll_entry->revents & POLLIN))
            return 0;
        if (http_receive_data(c) < 0)
            return -1;
        break;
    case HTTPSTATE_WAIT_FEED:
        /* no need to read if no events */
1025
        if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
Fabrice Bellard's avatar
Fabrice Bellard committed
1026 1027 1028 1029
            return -1;

        /* nothing to do, we'll be waken up by incoming feed packets */
        break;
1030 1031 1032 1033 1034 1035 1036 1037 1038

    case RTSPSTATE_SEND_REPLY:
        if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
            av_freep(&c->pb_buffer);
            return -1;
        }
        /* no need to write if no events */
        if (!(c->poll_entry->revents & POLLOUT))
            return 0;
1039
        len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
1040
        if (len < 0) {
1041 1042
            if (ff_neterrno() != AVERROR(EAGAIN) &&
                ff_neterrno() != AVERROR(EINTR)) {
1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056
                /* error : close connection */
                av_freep(&c->pb_buffer);
                return -1;
            }
        } else {
            c->buffer_ptr += len;
            c->data_count += len;
            if (c->buffer_ptr >= c->buffer_end) {
                /* all the buffer was sent : wait for a new request */
                av_freep(&c->pb_buffer);
                start_wait_request(c, 1);
            }
        }
        break;
1057 1058 1059 1060 1061 1062 1063 1064
    case RTSPSTATE_SEND_PACKET:
        if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
            av_freep(&c->packet_buffer);
            return -1;
        }
        /* no need to write if no events */
        if (!(c->poll_entry->revents & POLLOUT))
            return 0;
1065 1066
        len = send(c->fd, c->packet_buffer_ptr,
                    c->packet_buffer_end - c->packet_buffer_ptr, 0);
1067
        if (len < 0) {
1068 1069
            if (ff_neterrno() != AVERROR(EAGAIN) &&
                ff_neterrno() != AVERROR(EINTR)) {
1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082
                /* error : close connection */
                av_freep(&c->packet_buffer);
                return -1;
            }
        } else {
            c->packet_buffer_ptr += len;
            if (c->packet_buffer_ptr >= c->packet_buffer_end) {
                /* all the buffer was sent : wait for a new request */
                av_freep(&c->packet_buffer);
                c->state = RTSPSTATE_WAIT_REQUEST;
            }
        }
        break;
1083 1084 1085
    case HTTPSTATE_READY:
        /* nothing to do */
        break;
Fabrice Bellard's avatar
Fabrice Bellard committed
1086 1087 1088 1089 1090 1091
    default:
        return -1;
    }
    return 0;
}

1092 1093 1094 1095 1096
static int extract_rates(char *rates, int ratelen, const char *request)
{
    const char *p;

    for (p = request; *p && *p != '\r' && *p != '\n'; ) {
1097
        if (av_strncasecmp(p, "Pragma:", 7) == 0) {
1098 1099 1100 1101 1102
            const char *q = p + 7;

            while (*q && *q != '\n' && isspace(*q))
                q++;

1103
            if (av_strncasecmp(q, "stream-switch-entry=", 20) == 0) {
1104 1105 1106 1107 1108
                int stream_no;
                int rate_no;

                q += 20;

1109
                memset(rates, 0xff, ratelen);
1110 1111 1112 1113 1114

                while (1) {
                    while (*q && *q != '\n' && *q != ':')
                        q++;

1115
                    if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2)
1116
                        break;
1117

1118
                    stream_no--;
1119
                    if (stream_no < ratelen && stream_no >= 0)
1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138
                        rates[stream_no] = rate_no;

                    while (*q && *q != '\n' && !isspace(*q))
                        q++;
                }

                return 1;
            }
        }
        p = strchr(p, '\n');
        if (!p)
            break;

        p++;
    }

    return 0;
}

1139
static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
1140 1141
{
    int i;
1142 1143 1144 1145
    int best_bitrate = 100000000;
    int best = -1;

    for (i = 0; i < feed->nb_streams; i++) {
1146
        AVCodecContext *feed_codec = feed->streams[i]->codec;
1147 1148 1149 1150

        if (feed_codec->codec_id != codec->codec_id ||
            feed_codec->sample_rate != codec->sample_rate ||
            feed_codec->width != codec->width ||
1151
            feed_codec->height != codec->height)
1152 1153 1154 1155
            continue;

        /* Potential stream */

1156
        /* We want the fastest stream less than bit_rate, or the slowest
1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180
         * faster than bit_rate
         */

        if (feed_codec->bit_rate <= bit_rate) {
            if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) {
                best_bitrate = feed_codec->bit_rate;
                best = i;
            }
        } else {
            if (feed_codec->bit_rate < best_bitrate) {
                best_bitrate = feed_codec->bit_rate;
                best = i;
            }
        }
    }

    return best;
}

static int modify_current_stream(HTTPContext *c, char *rates)
{
    int i;
    FFStream *req = c->stream;
    int action_required = 0;
1181

1182 1183 1184 1185
    /* Not much we can do for a feed */
    if (!req->feed)
        return 0;

1186
    for (i = 0; i < req->nb_streams; i++) {
1187
        AVCodecContext *codec = req->streams[i]->codec;
1188 1189 1190

        switch(rates[i]) {
            case 0:
1191
                c->switch_feed_streams[i] = req->feed_streams[i];
1192 1193
                break;
            case 1:
1194
                c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
1195 1196
                break;
            case 2:
1197 1198 1199 1200 1201 1202 1203
                /* Wants off or slow */
                c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
#ifdef WANTS_OFF
                /* This doesn't work well when it turns off the only stream! */
                c->switch_feed_streams[i] = -2;
                c->feed_streams[i] = -2;
#endif
1204 1205 1206
                break;
        }

1207 1208 1209
        if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
            action_required = 1;
    }
1210

1211 1212
    return action_required;
}
1213

1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242
/* XXX: factorize in utils.c ? */
/* XXX: take care with different space meaning */
static void skip_spaces(const char **pp)
{
    const char *p;
    p = *pp;
    while (*p == ' ' || *p == '\t')
        p++;
    *pp = p;
}

static void get_word(char *buf, int buf_size, const char **pp)
{
    const char *p;
    char *q;

    p = *pp;
    skip_spaces(&p);
    q = buf;
    while (!isspace(*p) && *p != '\0') {
        if ((q - buf) < buf_size - 1)
            *q++ = *p;
        p++;
    }
    if (buf_size > 0)
        *q = '\0';
    *pp = p;
}

1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274
static void get_arg(char *buf, int buf_size, const char **pp)
{
    const char *p;
    char *q;
    int quote;

    p = *pp;
    while (isspace(*p)) p++;
    q = buf;
    quote = 0;
    if (*p == '\"' || *p == '\'')
        quote = *p++;
    for(;;) {
        if (quote) {
            if (*p == quote)
                break;
        } else {
            if (isspace(*p))
                break;
        }
        if (*p == '\0')
            break;
        if ((q - buf) < buf_size - 1)
            *q++ = *p;
        p++;
    }
    *q = '\0';
    if (quote && *p == quote)
        p++;
    *pp = p;
}

1275 1276 1277 1278 1279 1280 1281 1282
static void parse_acl_row(FFStream *stream, FFStream* feed, IPAddressACL *ext_acl,
                         const char *p, const char *filename, int line_num)
{
    char arg[1024];
    IPAddressACL acl;
    int errors = 0;

    get_arg(arg, sizeof(arg), &p);
1283
    if (av_strcasecmp(arg, "allow") == 0)
1284
        acl.action = IP_ALLOW;
1285
    else if (av_strcasecmp(arg, "deny") == 0)
1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369
        acl.action = IP_DENY;
    else {
        fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
                filename, line_num, arg);
        errors++;
    }

    get_arg(arg, sizeof(arg), &p);

    if (resolve_host(&acl.first, arg) != 0) {
        fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
                filename, line_num, arg);
        errors++;
    } else
        acl.last = acl.first;

    get_arg(arg, sizeof(arg), &p);

    if (arg[0]) {
        if (resolve_host(&acl.last, arg) != 0) {
            fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
                    filename, line_num, arg);
            errors++;
        }
    }

    if (!errors) {
        IPAddressACL *nacl = av_mallocz(sizeof(*nacl));
        IPAddressACL **naclp = 0;

        acl.next = 0;
        *nacl = acl;

        if (stream)
            naclp = &stream->acl;
        else if (feed)
            naclp = &feed->acl;
        else if (ext_acl)
            naclp = &ext_acl;
        else {
            fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
                    filename, line_num);
            errors++;
        }

        if (naclp) {
            while (*naclp)
                naclp = &(*naclp)->next;

            *naclp = nacl;
        }
    }
}


static IPAddressACL* parse_dynamic_acl(FFStream *stream, HTTPContext *c)
{
    FILE* f;
    char line[1024];
    char  cmd[1024];
    IPAddressACL *acl = NULL;
    int line_num = 0;
    const char *p;

    f = fopen(stream->dynamic_acl, "r");
    if (!f) {
        perror(stream->dynamic_acl);
        return NULL;
    }

    acl = av_mallocz(sizeof(IPAddressACL));

    /* Build ACL */
    for(;;) {
        if (fgets(line, sizeof(line), f) == NULL)
            break;
        line_num++;
        p = line;
        while (isspace(*p))
            p++;
        if (*p == '\0' || *p == '#')
            continue;
        get_arg(cmd, sizeof(cmd), &p);

1370
        if (!av_strcasecmp(cmd, "ACL"))
1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390
            parse_acl_row(NULL, NULL, acl, p, stream->dynamic_acl, line_num);
    }
    fclose(f);
    return acl;
}


static void free_acl_list(IPAddressACL *in_acl)
{
    IPAddressACL *pacl,*pacl2;

    pacl = in_acl;
    while(pacl) {
        pacl2 = pacl;
        pacl = pacl->next;
        av_freep(pacl2);
    }
}

static int validate_acl_list(IPAddressACL *in_acl, HTTPContext *c)
1391 1392 1393 1394
{
    enum IPAddressAction last_action = IP_DENY;
    IPAddressACL *acl;
    struct in_addr *src = &c->from_addr.sin_addr;
1395
    unsigned long src_addr = src->s_addr;
1396

1397
    for (acl = in_acl; acl; acl = acl->next) {
1398
        if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr)
1399 1400 1401 1402 1403 1404 1405 1406
            return (acl->action == IP_ALLOW) ? 1 : 0;
        last_action = acl->action;
    }

    /* Nothing matched, so return not the last action */
    return (last_action == IP_DENY) ? 1 : 0;
}

1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426
static int validate_acl(FFStream *stream, HTTPContext *c)
{
    int ret = 0;
    IPAddressACL *acl;


    /* if stream->acl is null validate_acl_list will return 1 */
    ret = validate_acl_list(stream->acl, c);

    if (stream->dynamic_acl[0]) {
        acl = parse_dynamic_acl(stream, c);

        ret = validate_acl_list(acl, c);

        free_acl_list(acl);
    }

    return ret;
}

1427 1428 1429 1430 1431 1432 1433 1434 1435 1436
/* compute the real filename of a file by matching it without its
   extensions to all the stream filenames */
static void compute_real_filename(char *filename, int max_size)
{
    char file1[1024];
    char file2[1024];
    char *p;
    FFStream *stream;

    /* compute filename by matching without the file extensions */
1437
    av_strlcpy(file1, filename, sizeof(file1));
1438 1439 1440 1441
    p = strrchr(file1, '.');
    if (p)
        *p = '\0';
    for(stream = first_stream; stream != NULL; stream = stream->next) {
1442
        av_strlcpy(file2, stream->filename, sizeof(file2));
1443 1444 1445 1446
        p = strrchr(file2, '.');
        if (p)
            *p = '\0';
        if (!strcmp(file1, file2)) {
1447
            av_strlcpy(filename, stream->filename, max_size);
1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461
            break;
        }
    }
}

enum RedirType {
    REDIR_NONE,
    REDIR_ASX,
    REDIR_RAM,
    REDIR_ASF,
    REDIR_RTSP,
    REDIR_SDP,
};

Fabrice Bellard's avatar
Fabrice Bellard committed
1462 1463 1464 1465
/* parse http request and prepare header */
static int http_parse_request(HTTPContext *c)
{
    char *p;
1466
    enum RedirType redir_type;
Fabrice Bellard's avatar
Fabrice Bellard committed
1467
    char cmd[32];
1468
    char info[1024], filename[1024];
Fabrice Bellard's avatar
Fabrice Bellard committed
1469 1470 1471 1472 1473
    char url[1024], *q;
    char protocol[32];
    char msg[1024];
    const char *mime_type;
    FFStream *stream;
1474
    int i;
1475
    char ratebuf[32];
1476
    char *useragent = 0;
Fabrice Bellard's avatar
Fabrice Bellard committed
1477 1478

    p = c->buffer;
1479
    get_word(cmd, sizeof(cmd), (const char **)&p);
1480
    av_strlcpy(c->method, cmd, sizeof(c->method));
1481

Fabrice Bellard's avatar
Fabrice Bellard committed
1482
    if (!strcmp(cmd, "GET"))
1483
        c->post = 0;
Fabrice Bellard's avatar
Fabrice Bellard committed
1484
    else if (!strcmp(cmd, "POST"))
1485
        c->post = 1;
Fabrice Bellard's avatar
Fabrice Bellard committed
1486 1487 1488
    else
        return -1;

1489
    get_word(url, sizeof(url), (const char **)&p);
1490
    av_strlcpy(c->url, url, sizeof(c->url));
1491

1492
    get_word(protocol, sizeof(protocol), (const char **)&p);
Fabrice Bellard's avatar
Fabrice Bellard committed
1493 1494
    if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
        return -1;
1495

1496
    av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
1497 1498

    if (ffserver_debug)
1499
        http_log("%s - - New connection: %s %s\n", inet_ntoa(c->from_addr.sin_addr), cmd, url);
1500

Fabrice Bellard's avatar
Fabrice Bellard committed
1501
    /* find the filename and the optional info string in the request */
1502
    p = strchr(url, '?');
Fabrice Bellard's avatar
Fabrice Bellard committed
1503
    if (p) {
1504
        av_strlcpy(info, p, sizeof(info));
Fabrice Bellard's avatar
Fabrice Bellard committed
1505
        *p = '\0';
1506
    } else
Fabrice Bellard's avatar
Fabrice Bellard committed
1507 1508
        info[0] = '\0';

1509
    av_strlcpy(filename, url + ((*url == '/') ? 1 : 0), sizeof(filename)-1);
1510

1511
    for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1512
        if (av_strncasecmp(p, "User-Agent:", 11) == 0) {
1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524
            useragent = p + 11;
            if (*useragent && *useragent != '\n' && isspace(*useragent))
                useragent++;
            break;
        }
        p = strchr(p, '\n');
        if (!p)
            break;

        p++;
    }

1525
    redir_type = REDIR_NONE;
1526
    if (av_match_ext(filename, "asx")) {
1527
        redir_type = REDIR_ASX;
1528
        filename[strlen(filename)-1] = 'f';
1529
    } else if (av_match_ext(filename, "asf") &&
1530
        (!useragent || av_strncasecmp(useragent, "NSPlayer", 8) != 0)) {
1531
        /* if this isn't WMP or lookalike, return the redirector file */
1532
        redir_type = REDIR_ASF;
1533
    } else if (av_match_ext(filename, "rpm,ram")) {
1534
        redir_type = REDIR_RAM;
1535
        strcpy(filename + strlen(filename)-2, "m");
1536
    } else if (av_match_ext(filename, "rtsp")) {
1537
        redir_type = REDIR_RTSP;
1538
        compute_real_filename(filename, sizeof(filename) - 1);
1539
    } else if (av_match_ext(filename, "sdp")) {
1540
        redir_type = REDIR_SDP;
1541
        compute_real_filename(filename, sizeof(filename) - 1);
1542
    }
1543

1544 1545
    // "redirect" / request to index.html
    if (!strlen(filename))
1546
        av_strlcpy(filename, "index.html", sizeof(filename) - 1);
1547

Fabrice Bellard's avatar
Fabrice Bellard committed
1548 1549
    stream = first_stream;
    while (stream != NULL) {
1550
        if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
Fabrice Bellard's avatar
Fabrice Bellard committed
1551 1552 1553 1554
            break;
        stream = stream->next;
    }
    if (stream == NULL) {
1555
        snprintf(msg, sizeof(msg), "File '%s' not found", url);
1556
        http_log("File '%s' not found\n", url);
Fabrice Bellard's avatar
Fabrice Bellard committed
1557 1558
        goto send_error;
    }
1559

1560 1561 1562 1563 1564 1565 1566
    c->stream = stream;
    memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
    memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));

    if (stream->stream_type == STREAM_TYPE_REDIRECT) {
        c->http_error = 301;
        q = c->buffer;
1567 1568 1569 1570 1571 1572 1573 1574
        q += snprintf(q, c->buffer_size,
                      "HTTP/1.0 301 Moved\r\n"
                      "Location: %s\r\n"
                      "Content-type: text/html\r\n"
                      "\r\n"
                      "<html><head><title>Moved</title></head><body>\r\n"
                      "You should be <a href=\"%s\">redirected</a>.\r\n"
                      "</body></html>\r\n", stream->feed_filename, stream->feed_filename);
1575 1576 1577 1578 1579 1580 1581
        /* prepare output buffer */
        c->buffer_ptr = c->buffer;
        c->buffer_end = q;
        c->state = HTTPSTATE_SEND_HEADER;
        return 0;
    }

1582 1583
    /* If this is WMP, get the rate information */
    if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1584
        if (modify_current_stream(c, ratebuf)) {
1585
            for (i = 0; i < FF_ARRAY_ELEMS(c->feed_streams); i++) {
1586
                if (c->switch_feed_streams[i] >= 0)
Reinhard Tartler's avatar
Reinhard Tartler committed
1587
                    c->switch_feed_streams[i] = -1;
1588 1589
            }
        }
1590 1591
    }

1592 1593 1594
    if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE)
        current_bandwidth += stream->bandwidth;

Diego Biurrun's avatar
Diego Biurrun committed
1595
    /* If already streaming this feed, do not let start another feeder. */
1596 1597
    if (stream->feed_opened) {
        snprintf(msg, sizeof(msg), "This feed is already being received.");
1598
        http_log("Feed '%s' already being received\n", stream->feed_filename);
1599 1600 1601
        goto send_error;
    }

1602
    if (c->post == 0 && max_bandwidth < current_bandwidth) {
1603
        c->http_error = 503;
1604
        q = c->buffer;
1605
        q += snprintf(q, c->buffer_size,
1606
                      "HTTP/1.0 503 Server too busy\r\n"
1607 1608 1609 1610
                      "Content-type: text/html\r\n"
                      "\r\n"
                      "<html><head><title>Too busy</title></head><body>\r\n"
                      "<p>The server is too busy to serve your request at this time.</p>\r\n"
1611 1612
                      "<p>The bandwidth being served (including your stream) is %"PRIu64"kbit/sec, "
                      "and this exceeds the limit of %"PRIu64"kbit/sec.</p>\r\n"
1613
                      "</body></html>\r\n", current_bandwidth, max_bandwidth);
1614 1615 1616 1617 1618 1619
        /* prepare output buffer */
        c->buffer_ptr = c->buffer;
        c->buffer_end = q;
        c->state = HTTPSTATE_SEND_HEADER;
        return 0;
    }
1620

1621
    if (redir_type != REDIR_NONE) {
1622
        char *hostinfo = 0;
1623

1624
        for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1625
            if (av_strncasecmp(p, "Host:", 5) == 0) {
1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653
                hostinfo = p + 5;
                break;
            }
            p = strchr(p, '\n');
            if (!p)
                break;

            p++;
        }

        if (hostinfo) {
            char *eoh;
            char hostbuf[260];

            while (isspace(*hostinfo))
                hostinfo++;

            eoh = strchr(hostinfo, '\n');
            if (eoh) {
                if (eoh[-1] == '\r')
                    eoh--;

                if (eoh - hostinfo < sizeof(hostbuf) - 1) {
                    memcpy(hostbuf, hostinfo, eoh - hostinfo);
                    hostbuf[eoh - hostinfo] = 0;

                    c->http_error = 200;
                    q = c->buffer;
1654 1655
                    switch(redir_type) {
                    case REDIR_ASX:
1656 1657 1658 1659 1660 1661 1662 1663
                        q += snprintf(q, c->buffer_size,
                                      "HTTP/1.0 200 ASX Follows\r\n"
                                      "Content-type: video/x-ms-asf\r\n"
                                      "\r\n"
                                      "<ASX Version=\"3\">\r\n"
                                      //"<!-- Autogenerated by ffserver -->\r\n"
                                      "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n"
                                      "</ASX>\r\n", hostbuf, filename, info);
1664 1665
                        break;
                    case REDIR_RAM:
1666 1667 1668 1669 1670 1671
                        q += snprintf(q, c->buffer_size,
                                      "HTTP/1.0 200 RAM Follows\r\n"
                                      "Content-type: audio/x-pn-realaudio\r\n"
                                      "\r\n"
                                      "# Autogenerated by ffserver\r\n"
                                      "http://%s/%s%s\r\n", hostbuf, filename, info);
1672 1673
                        break;
                    case REDIR_ASF:
1674 1675 1676 1677 1678 1679
                        q += snprintf(q, c->buffer_size,
                                      "HTTP/1.0 200 ASF Redirect follows\r\n"
                                      "Content-type: video/x-ms-asf\r\n"
                                      "\r\n"
                                      "[Reference]\r\n"
                                      "Ref1=http://%s/%s%s\r\n", hostbuf, filename, info);
1680 1681 1682 1683 1684
                        break;
                    case REDIR_RTSP:
                        {
                            char hostname[256], *p;
                            /* extract only hostname */
1685
                            av_strlcpy(hostname, hostbuf, sizeof(hostname));
1686 1687 1688
                            p = strrchr(hostname, ':');
                            if (p)
                                *p = '\0';
1689 1690 1691 1692 1693 1694
                            q += snprintf(q, c->buffer_size,
                                          "HTTP/1.0 200 RTSP Redirect follows\r\n"
                                          /* XXX: incorrect mime type ? */
                                          "Content-type: application/x-rtsp\r\n"
                                          "\r\n"
                                          "rtsp://%s:%d/%s\r\n", hostname, ntohs(my_rtsp_addr.sin_port), filename);
1695 1696 1697 1698
                        }
                        break;
                    case REDIR_SDP:
                        {
1699
                            uint8_t *sdp_data;
1700 1701 1702
                            int sdp_data_size, len;
                            struct sockaddr_in my_addr;

1703 1704 1705 1706
                            q += snprintf(q, c->buffer_size,
                                          "HTTP/1.0 200 OK\r\n"
                                          "Content-type: application/sdp\r\n"
                                          "\r\n");
1707 1708 1709

                            len = sizeof(my_addr);
                            getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
1710

1711
                            /* XXX: should use a dynamic buffer */
1712 1713
                            sdp_data_size = prepare_sdp_description(stream,
                                                                    &sdp_data,
1714 1715 1716 1717 1718 1719 1720 1721 1722 1723
                                                                    my_addr.sin_addr);
                            if (sdp_data_size > 0) {
                                memcpy(q, sdp_data, sdp_data_size);
                                q += sdp_data_size;
                                *q = '\0';
                                av_free(sdp_data);
                            }
                        }
                        break;
                    default:
1724
                        abort();
1725
                        break;
1726
                    }
1727 1728 1729 1730 1731 1732 1733 1734 1735 1736

                    /* prepare output buffer */
                    c->buffer_ptr = c->buffer;
                    c->buffer_end = q;
                    c->state = HTTPSTATE_SEND_HEADER;
                    return 0;
                }
            }
        }

1737
        snprintf(msg, sizeof(msg), "ASX/RAM file not handled");
1738
        goto send_error;
Fabrice Bellard's avatar
Fabrice Bellard committed
1739 1740
    }

1741
    stream->conns_served++;
1742

Fabrice Bellard's avatar
Fabrice Bellard committed
1743 1744
    /* XXX: add there authenticate and IP match */

1745
    if (c->post) {
Fabrice Bellard's avatar
Fabrice Bellard committed
1746 1747
        /* if post, it means a feed is being sent */
        if (!stream->is_feed) {
Diego Biurrun's avatar
Diego Biurrun committed
1748 1749
            /* However it might be a status report from WMP! Let us log the
             * data as it might come in handy one day. */
1750
            char *logline = 0;
1751
            int client_id = 0;
1752

1753
            for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1754
                if (av_strncasecmp(p, "Pragma: log-line=", 17) == 0) {
1755 1756 1757
                    logline = p;
                    break;
                }
1758
                if (av_strncasecmp(p, "Pragma: client-id=", 18) == 0)
1759
                    client_id = strtol(p + 18, 0, 10);
1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774
                p = strchr(p, '\n');
                if (!p)
                    break;

                p++;
            }

            if (logline) {
                char *eol = strchr(logline, '\n');

                logline += 17;

                if (eol) {
                    if (eol[-1] == '\r')
                        eol--;
Falk Hüffner's avatar
Falk Hüffner committed
1775
                    http_log("%.*s\n", (int) (eol - logline), logline);
1776 1777 1778
                    c->suppress_log = 1;
                }
            }
1779

1780
#ifdef DEBUG
1781
            http_log("\nGot request:\n%s\n", c->buffer);
1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792
#endif

            if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
                HTTPContext *wmpc;

                /* Now we have to find the client_id */
                for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
                    if (wmpc->wmp_client_id == client_id)
                        break;
                }

1793 1794
                if (wmpc && modify_current_stream(wmpc, ratebuf))
                    wmpc->switch_pending = 1;
1795
            }
1796

1797
            snprintf(msg, sizeof(msg), "POST command not handled");
1798
            c->stream = 0;
Fabrice Bellard's avatar
Fabrice Bellard committed
1799 1800 1801
            goto send_error;
        }
        if (http_start_receive_data(c) < 0) {
1802
            snprintf(msg, sizeof(msg), "could not open feed");
Fabrice Bellard's avatar
Fabrice Bellard committed
1803 1804 1805 1806 1807 1808 1809
            goto send_error;
        }
        c->http_error = 0;
        c->state = HTTPSTATE_RECEIVE_DATA;
        return 0;
    }

1810
#ifdef DEBUG
1811
    if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0)
1812
        http_log("\nGot request:\n%s\n", c->buffer);
1813 1814
#endif

Fabrice Bellard's avatar
Fabrice Bellard committed
1815
    if (c->stream->stream_type == STREAM_TYPE_STATUS)
1816
        goto send_status;
Fabrice Bellard's avatar
Fabrice Bellard committed
1817 1818 1819

    /* open input stream */
    if (open_input_stream(c, info) < 0) {
1820
        snprintf(msg, sizeof(msg), "Input stream corresponding to '%s' not found", url);
Fabrice Bellard's avatar
Fabrice Bellard committed
1821 1822 1823 1824 1825
        goto send_error;
    }

    /* prepare http header */
    q = c->buffer;
1826
    q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
Fabrice Bellard's avatar
Fabrice Bellard committed
1827 1828
    mime_type = c->stream->fmt->mime_type;
    if (!mime_type)
1829
        mime_type = "application/x-octet-stream";
1830
    q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Pragma: no-cache\r\n");
Fabrice Bellard's avatar
Fabrice Bellard committed
1831 1832

    /* for asf, we need extra headers */
1833
    if (!strcmp(c->stream->fmt->name,"asf_stream")) {
1834 1835
        /* Need to allocate a client id */

1836
        c->wmp_client_id = av_lfg_get(&random_state);
1837

1838
        q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Server: Cougar 4.1.0.3923\r\nCache-Control: no-cache\r\nPragma: client-id=%d\r\nPragma: features=\"broadcast\"\r\n", c->wmp_client_id);
Fabrice Bellard's avatar
Fabrice Bellard committed
1839
    }
1840 1841
    q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-Type: %s\r\n", mime_type);
    q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1842

Fabrice Bellard's avatar
Fabrice Bellard committed
1843 1844 1845 1846 1847 1848 1849 1850 1851
    /* prepare output buffer */
    c->http_error = 0;
    c->buffer_ptr = c->buffer;
    c->buffer_end = q;
    c->state = HTTPSTATE_SEND_HEADER;
    return 0;
 send_error:
    c->http_error = 404;
    q = c->buffer;
1852 1853 1854 1855
    q += snprintf(q, c->buffer_size,
                  "HTTP/1.0 404 Not Found\r\n"
                  "Content-type: text/html\r\n"
                  "\r\n"
1856 1857 1858 1859
                  "<html>\n"
                  "<head><title>404 Not Found</title></head>\n"
                  "<body>%s</body>\n"
                  "</html>\n", msg);
Fabrice Bellard's avatar
Fabrice Bellard committed
1860 1861 1862 1863 1864
    /* prepare output buffer */
    c->buffer_ptr = c->buffer;
    c->buffer_end = q;
    c->state = HTTPSTATE_SEND_HEADER;
    return 0;
1865 1866
 send_status:
    compute_status(c);
Fabrice Bellard's avatar
Fabrice Bellard committed
1867 1868 1869 1870 1871 1872
    c->http_error = 200; /* horrible : we use this value to avoid
                            going to the send data state */
    c->state = HTTPSTATE_SEND_HEADER;
    return 0;
}

1873
static void fmt_bytecount(AVIOContext *pb, int64_t count)
1874 1875 1876 1877
{
    static const char *suffix = " kMGTP";
    const char *s;

1878
    for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++);
1879

1880
    avio_printf(pb, "%"PRId64"%c", count, *s);
1881 1882
}

1883
static void compute_status(HTTPContext *c)
Fabrice Bellard's avatar
Fabrice Bellard committed
1884 1885 1886
{
    HTTPContext *c1;
    FFStream *stream;
1887
    char *p;
Fabrice Bellard's avatar
Fabrice Bellard committed
1888
    time_t ti;
1889
    int i, len;
1890
    AVIOContext *pb;
1891

1892
    if (avio_open_dyn_buf(&pb) < 0) {
1893
        /* XXX: return an error ? */
1894
        c->buffer_ptr = c->buffer;
1895 1896
        c->buffer_end = c->buffer;
        return;
1897
    }
Fabrice Bellard's avatar
Fabrice Bellard committed
1898

1899 1900 1901 1902
    avio_printf(pb, "HTTP/1.0 200 OK\r\n");
    avio_printf(pb, "Content-type: %s\r\n", "text/html");
    avio_printf(pb, "Pragma: no-cache\r\n");
    avio_printf(pb, "\r\n");
1903

1904
    avio_printf(pb, "<html><head><title>%s Status</title>\n", program_name);
1905
    if (c->stream->feed_filename[0])
1906 1907 1908
        avio_printf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
    avio_printf(pb, "</head>\n<body>");
    avio_printf(pb, "<h1>%s Status</h1>\n", program_name);
Fabrice Bellard's avatar
Fabrice Bellard committed
1909
    /* format status */
1910 1911 1912
    avio_printf(pb, "<h2>Available Streams</h2>\n");
    avio_printf(pb, "<table cellspacing=0 cellpadding=4>\n");
    avio_printf(pb, "<tr><th valign=top>Path<th align=left>Served<br>Conns<th><br>bytes<th valign=top>Format<th>Bit rate<br>kbits/s<th align=left>Video<br>kbits/s<th><br>Codec<th align=left>Audio<br>kbits/s<th><br>Codec<th align=left valign=top>Feed\n");
Fabrice Bellard's avatar
Fabrice Bellard committed
1913 1914
    stream = first_stream;
    while (stream != NULL) {
1915 1916 1917
        char sfilename[1024];
        char *eosf;

1918
        if (stream->feed != stream) {
1919
            av_strlcpy(sfilename, stream->filename, sizeof(sfilename) - 10);
1920 1921
            eosf = sfilename + strlen(sfilename);
            if (eosf - sfilename >= 4) {
1922
                if (strcmp(eosf - 4, ".asf") == 0)
1923
                    strcpy(eosf - 4, ".asx");
1924
                else if (strcmp(eosf - 3, ".rm") == 0)
1925
                    strcpy(eosf - 3, ".ram");
1926
                else if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
1927 1928 1929
                    /* generate a sample RTSP director if
                       unicast. Generate an SDP redirector if
                       multicast */
1930 1931 1932
                    eosf = strrchr(sfilename, '.');
                    if (!eosf)
                        eosf = sfilename + strlen(sfilename);
1933 1934 1935 1936
                    if (stream->is_multicast)
                        strcpy(eosf, ".sdp");
                    else
                        strcpy(eosf, ".rtsp");
1937
                }
1938
            }
1939

1940
            avio_printf(pb, "<tr><td><a href=\"/%s\">%s</a> ",
1941
                         sfilename, stream->filename);
1942
            avio_printf(pb, "<td align=right> %d <td align=right> ",
1943
                        stream->conns_served);
1944
            fmt_bytecount(pb, stream->bytes_served);
1945
            switch(stream->stream_type) {
1946
            case STREAM_TYPE_LIVE: {
1947 1948
                    int audio_bit_rate = 0;
                    int video_bit_rate = 0;
Zdenek Kabelac's avatar
Zdenek Kabelac committed
1949 1950 1951 1952
                    const char *audio_codec_name = "";
                    const char *video_codec_name = "";
                    const char *audio_codec_name_extra = "";
                    const char *video_codec_name_extra = "";
1953 1954 1955

                    for(i=0;i<stream->nb_streams;i++) {
                        AVStream *st = stream->streams[i];
1956 1957
                        AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
                        switch(st->codec->codec_type) {
1958
                        case AVMEDIA_TYPE_AUDIO:
1959
                            audio_bit_rate += st->codec->bit_rate;
1960 1961 1962 1963 1964 1965
                            if (codec) {
                                if (*audio_codec_name)
                                    audio_codec_name_extra = "...";
                                audio_codec_name = codec->name;
                            }
                            break;
1966
                        case AVMEDIA_TYPE_VIDEO:
1967
                            video_bit_rate += st->codec->bit_rate;
1968 1969 1970 1971 1972 1973
                            if (codec) {
                                if (*video_codec_name)
                                    video_codec_name_extra = "...";
                                video_codec_name = codec->name;
                            }
                            break;
1974
                        case AVMEDIA_TYPE_DATA:
1975
                            video_bit_rate += st->codec->bit_rate;
1976
                            break;
1977
                        default:
1978
                            abort();
1979
                        }
Fabrice Bellard's avatar
Fabrice Bellard committed
1980
                    }
1981
                    avio_printf(pb, "<td align=center> %s <td align=right> %d <td align=right> %d <td> %s %s <td align=right> %d <td> %s %s",
1982
                                 stream->fmt->name,
1983
                                 stream->bandwidth,
1984 1985
                                 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
                                 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
1986
                    if (stream->feed)
1987
                        avio_printf(pb, "<td>%s", stream->feed->filename);
1988
                    else
1989 1990
                        avio_printf(pb, "<td>%s", stream->feed_filename);
                    avio_printf(pb, "\n");
Fabrice Bellard's avatar
Fabrice Bellard committed
1991
                }
1992 1993
                break;
            default:
1994
                avio_printf(pb, "<td align=center> - <td align=right> - <td align=right> - <td><td align=right> - <td>\n");
1995
                break;
Fabrice Bellard's avatar
Fabrice Bellard committed
1996 1997 1998 1999
            }
        }
        stream = stream->next;
    }
2000
    avio_printf(pb, "</table>\n");
2001 2002 2003 2004

    stream = first_stream;
    while (stream != NULL) {
        if (stream->feed == stream) {
2005
            avio_printf(pb, "<h2>Feed %s</h2>", stream->filename);
2006
            if (stream->pid) {
2007
                avio_printf(pb, "Running as pid %d.\n", stream->pid);
2008

2009 2010 2011 2012 2013 2014
#if defined(linux) && !defined(CONFIG_NOCUTILS)
                {
                    FILE *pid_stat;
                    char ps_cmd[64];

                    /* This is somewhat linux specific I guess */
2015 2016
                    snprintf(ps_cmd, sizeof(ps_cmd),
                             "ps -o \"%%cpu,cputime\" --no-headers %d",
2017
                             stream->pid);
2018

2019 2020 2021 2022
                    pid_stat = popen(ps_cmd, "r");
                    if (pid_stat) {
                        char cpuperc[10];
                        char cpuused[64];
2023 2024

                        if (fscanf(pid_stat, "%10s %64s", cpuperc,
2025
                                   cpuused) == 2) {
2026
                            avio_printf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
2027 2028 2029
                                         cpuperc, cpuused);
                        }
                        fclose(pid_stat);
2030 2031 2032 2033
                    }
                }
#endif

2034
                avio_printf(pb, "<p>");
2035
            }
2036
            avio_printf(pb, "<table cellspacing=0 cellpadding=4><tr><th>Stream<th>type<th>kbits/s<th align=left>codec<th align=left>Parameters\n");
2037 2038 2039

            for (i = 0; i < stream->nb_streams; i++) {
                AVStream *st = stream->streams[i];
2040
                AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
2041
                const char *type = "unknown";
2042 2043 2044
                char parameters[64];

                parameters[0] = 0;
2045

2046
                switch(st->codec->codec_type) {
2047
                case AVMEDIA_TYPE_AUDIO:
2048
                    type = "audio";
2049
                    snprintf(parameters, sizeof(parameters), "%d channel(s), %d Hz", st->codec->channels, st->codec->sample_rate);
2050
                    break;
2051
                case AVMEDIA_TYPE_VIDEO:
2052
                    type = "video";
2053 2054
                    snprintf(parameters, sizeof(parameters), "%dx%d, q=%d-%d, fps=%d", st->codec->width, st->codec->height,
                                st->codec->qmin, st->codec->qmax, st->codec->time_base.den / st->codec->time_base.num);
2055 2056
                    break;
                default:
2057
                    abort();
2058
                }
2059
                avio_printf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
2060
                        i, type, st->codec->bit_rate/1000, codec ? codec->name : "", parameters);
2061
            }
2062
            avio_printf(pb, "</table>\n");
2063

2064
        }
2065 2066
        stream = stream->next;
    }
2067

Fabrice Bellard's avatar
Fabrice Bellard committed
2068
    /* connection status */
2069
    avio_printf(pb, "<h2>Connection Status</h2>\n");
Fabrice Bellard's avatar
Fabrice Bellard committed
2070

2071
    avio_printf(pb, "Number of connections: %d / %d<br>\n",
Fabrice Bellard's avatar
Fabrice Bellard committed
2072 2073
                 nb_connections, nb_max_connections);

2074
    avio_printf(pb, "Bandwidth in use: %"PRIu64"k / %"PRIu64"k<br>\n",
2075
                 current_bandwidth, max_bandwidth);
2076

2077 2078
    avio_printf(pb, "<table>\n");
    avio_printf(pb, "<tr><th>#<th>File<th>IP<th>Proto<th>State<th>Target bits/sec<th>Actual bits/sec<th>Bytes transferred\n");
Fabrice Bellard's avatar
Fabrice Bellard committed
2079 2080
    c1 = first_http_ctx;
    i = 0;
2081
    while (c1 != NULL) {
2082 2083 2084 2085
        int bitrate;
        int j;

        bitrate = 0;
2086 2087
        if (c1->stream) {
            for (j = 0; j < c1->stream->nb_streams; j++) {
2088
                if (!c1->stream->feed)
2089
                    bitrate += c1->stream->streams[j]->codec->bit_rate;
2090 2091
                else if (c1->feed_streams[j] >= 0)
                    bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec->bit_rate;
2092 2093 2094
            }
        }

Fabrice Bellard's avatar
Fabrice Bellard committed
2095 2096
        i++;
        p = inet_ntoa(c1->from_addr.sin_addr);
2097
        avio_printf(pb, "<tr><td><b>%d</b><td>%s%s<td>%s<td>%s<td>%s<td align=right>",
2098 2099
                    i,
                    c1->stream ? c1->stream->filename : "",
2100
                    c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
2101
                    p,
2102 2103 2104
                    c1->protocol,
                    http_state[c1->state]);
        fmt_bytecount(pb, bitrate);
2105
        avio_printf(pb, "<td align=right>");
2106
        fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
2107
        avio_printf(pb, "<td align=right>");
2108
        fmt_bytecount(pb, c1->data_count);
2109
        avio_printf(pb, "\n");
Fabrice Bellard's avatar
Fabrice Bellard committed
2110 2111
        c1 = c1->next;
    }
2112
    avio_printf(pb, "</table>\n");
2113

Fabrice Bellard's avatar
Fabrice Bellard committed
2114 2115 2116
    /* date */
    ti = time(NULL);
    p = ctime(&ti);
2117 2118
    avio_printf(pb, "<hr size=1 noshade>Generated at %s", p);
    avio_printf(pb, "</body>\n</html>\n");
Fabrice Bellard's avatar
Fabrice Bellard committed
2119

2120
    len = avio_close_dyn_buf(pb, &c->pb_buffer);
2121 2122
    c->buffer_ptr = c->pb_buffer;
    c->buffer_end = c->pb_buffer + len;
Fabrice Bellard's avatar
Fabrice Bellard committed
2123 2124 2125 2126 2127 2128
}

static int open_input_stream(HTTPContext *c, const char *info)
{
    char buf[128];
    char input_filename[1024];
2129
    AVFormatContext *s = NULL;
2130
    int i, ret;
2131
    int64_t stream_pos;
Fabrice Bellard's avatar
Fabrice Bellard committed
2132 2133 2134 2135 2136

    /* find file name */
    if (c->stream->feed) {
        strcpy(input_filename, c->stream->feed->feed_filename);
        /* compute position (absolute time) */
2137
        if (av_find_info_tag(buf, sizeof(buf), "date", info)) {
2138 2139
            if ((ret = av_parse_time(&stream_pos, buf, 0)) < 0)
                return ret;
2140
        } else if (av_find_info_tag(buf, sizeof(buf), "buffer", info)) {
2141
            int prebuffer = strtol(buf, 0, 10);
2142
            stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
2143
        } else
2144
            stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
Fabrice Bellard's avatar
Fabrice Bellard committed
2145 2146 2147
    } else {
        strcpy(input_filename, c->stream->feed_filename);
        /* compute position (relative time) */
2148
        if (av_find_info_tag(buf, sizeof(buf), "date", info)) {
2149 2150
            if ((ret = av_parse_time(&stream_pos, buf, 1)) < 0)
                return ret;
2151
        } else
Fabrice Bellard's avatar
Fabrice Bellard committed
2152 2153 2154 2155 2156 2157
            stream_pos = 0;
    }
    if (input_filename[0] == '\0')
        return -1;

    /* open stream */
2158
    if ((ret = avformat_open_input(&s, input_filename, c->stream->ifmt, &c->stream->in_opts)) < 0) {
2159
        http_log("could not open %s: %d\n", input_filename, ret);
Fabrice Bellard's avatar
Fabrice Bellard committed
2160
        return -1;
2161
    }
2162
    s->flags |= AVFMT_FLAG_GENPTS;
Fabrice Bellard's avatar
Fabrice Bellard committed
2163
    c->fmt_in = s;
2164
    if (strcmp(s->iformat->name, "ffm") && avformat_find_stream_info(c->fmt_in, NULL) < 0) {
2165
        http_log("Could not find stream info '%s'\n", input_filename);
2166
        avformat_close_input(&s);
2167 2168
        return -1;
    }
2169

2170 2171 2172 2173
    /* choose stream as clock source (we favorize video stream if
       present) for packet sending */
    c->pts_stream_index = 0;
    for(i=0;i<c->stream->nb_streams;i++) {
2174
        if (c->pts_stream_index == 0 &&
2175
            c->stream->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
2176 2177 2178
            c->pts_stream_index = i;
        }
    }
Fabrice Bellard's avatar
Fabrice Bellard committed
2179

2180
    if (c->fmt_in->iformat->read_seek)
2181
        av_seek_frame(c->fmt_in, -1, stream_pos, 0);
2182 2183 2184
    /* set the start time (needed for maxtime and RTP packet timing) */
    c->start_time = cur_time;
    c->first_pts = AV_NOPTS_VALUE;
Fabrice Bellard's avatar
Fabrice Bellard committed
2185 2186 2187
    return 0;
}

2188 2189
/* return the server clock (in us) */
static int64_t get_server_clock(HTTPContext *c)
2190
{
2191
    /* compute current pts value from system time */
2192
    return (cur_time - c->start_time) * 1000;
2193 2194
}

2195 2196 2197
/* return the estimated time at which the current packet must be sent
   (in us) */
static int64_t get_packet_send_clock(HTTPContext *c)
2198
{
2199
    int bytes_left, bytes_sent, frame_bytes;
2200

2201
    frame_bytes = c->cur_frame_bytes;
2202
    if (frame_bytes <= 0)
2203
        return c->cur_pts;
2204
    else {
2205 2206 2207
        bytes_left = c->buffer_end - c->buffer_ptr;
        bytes_sent = frame_bytes - bytes_left;
        return c->cur_pts + (c->cur_frame_duration * bytes_sent) / frame_bytes;
2208 2209 2210 2211 2212 2213 2214 2215 2216
    }
}


static int http_prepare_data(HTTPContext *c)
{
    int i, len, ret;
    AVFormatContext *ctx;

2217
    av_freep(&c->pb_buffer);
2218 2219 2220
    switch(c->state) {
    case HTTPSTATE_SEND_DATA_HEADER:
        memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
2221 2222 2223 2224
        av_dict_set(&c->fmt_ctx.metadata, "author"   , c->stream->author   , 0);
        av_dict_set(&c->fmt_ctx.metadata, "comment"  , c->stream->comment  , 0);
        av_dict_set(&c->fmt_ctx.metadata, "copyright", c->stream->copyright, 0);
        av_dict_set(&c->fmt_ctx.metadata, "title"    , c->stream->title    , 0);
2225

2226 2227
        c->fmt_ctx.streams = av_mallocz(sizeof(AVStream *) * c->stream->nb_streams);

2228
        for(i=0;i<c->stream->nb_streams;i++) {
2229
            AVStream *src;
2230
            c->fmt_ctx.streams[i] = av_mallocz(sizeof(AVStream));
2231
            /* if file or feed, then just take streams from FFStream struct */
2232
            if (!c->stream->feed ||
2233
                c->stream->feed == c->stream)
2234
                src = c->stream->streams[i];
2235
            else
2236 2237
                src = c->stream->feed->streams[c->stream->feed_streams[i]];

2238 2239 2240
            *(c->fmt_ctx.streams[i]) = *src;
            c->fmt_ctx.streams[i]->priv_data = 0;
            c->fmt_ctx.streams[i]->codec->frame_number = 0; /* XXX: should be done in
2241 2242
                                           AVStream, not in codec */
        }
2243 2244 2245 2246
        /* set output format parameters */
        c->fmt_ctx.oformat = c->stream->fmt;
        c->fmt_ctx.nb_streams = c->stream->nb_streams;

2247 2248 2249
        c->got_key_frame = 0;

        /* prepare header and save header data in a stream */
2250
        if (avio_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2251 2252 2253
            /* XXX: potential leak */
            return -1;
        }
2254
        c->fmt_ctx.pb->seekable = 0;
2255

2256 2257 2258 2259 2260 2261 2262
        /*
         * HACK to avoid mpeg ps muxer to spit many underflow errors
         * Default value from FFmpeg
         * Try to set it use configuration option
         */
        c->fmt_ctx.max_delay = (int)(0.7*AV_TIME_BASE);

2263
        if (avformat_write_header(&c->fmt_ctx, NULL) < 0) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2264
            http_log("Error writing output header\n");
2265
            return -1;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2266
        }
2267
        av_dict_free(&c->fmt_ctx.metadata);
2268

2269
        len = avio_close_dyn_buf(c->fmt_ctx.pb, &c->pb_buffer);
2270 2271 2272 2273
        c->buffer_ptr = c->pb_buffer;
        c->buffer_end = c->pb_buffer + len;

        c->state = HTTPSTATE_SEND_DATA;
Fabrice Bellard's avatar
Fabrice Bellard committed
2274 2275 2276 2277
        c->last_packet_sent = 0;
        break;
    case HTTPSTATE_SEND_DATA:
        /* find a new packet */
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290
        /* read a packet from the input stream */
        if (c->stream->feed)
            ffm_set_write_index(c->fmt_in,
                                c->stream->feed->feed_write_index,
                                c->stream->feed->feed_size);

        if (c->stream->max_time &&
            c->stream->max_time + c->start_time - cur_time < 0)
            /* We have timed out */
            c->state = HTTPSTATE_SEND_DATA_TRAILER;
        else {
            AVPacket pkt;
        redo:
2291 2292 2293
            ret = av_read_frame(c->fmt_in, &pkt);
            if (ret < 0) {
                if (c->stream->feed) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2294 2295 2296 2297
                    /* if coming from feed, it means we reached the end of the
                       ffm file, so must wait for more data */
                    c->state = HTTPSTATE_WAIT_FEED;
                    return 1; /* state changed */
2298 2299 2300
                } else if (ret == AVERROR(EAGAIN)) {
                    /* input not ready, come back later */
                    return 0;
2301
                } else {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2302
                    if (c->stream->loop) {
2303
                        avformat_close_input(&c->fmt_in);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2304 2305 2306 2307 2308 2309 2310
                        if (open_input_stream(c, "") < 0)
                            goto no_loop;
                        goto redo;
                    } else {
                    no_loop:
                        /* must send trailer now because eof or error */
                        c->state = HTTPSTATE_SEND_DATA_TRAILER;
2311
                    }
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2312 2313
                }
            } else {
2314
                int source_index = pkt.stream_index;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2315 2316 2317 2318 2319 2320 2321 2322 2323 2324
                /* update first pts if needed */
                if (c->first_pts == AV_NOPTS_VALUE) {
                    c->first_pts = av_rescale_q(pkt.dts, c->fmt_in->streams[pkt.stream_index]->time_base, AV_TIME_BASE_Q);
                    c->start_time = cur_time;
                }
                /* send it to the appropriate stream */
                if (c->stream->feed) {
                    /* if coming from a feed, select the right stream */
                    if (c->switch_pending) {
                        c->switch_pending = 0;
2325
                        for(i=0;i<c->stream->nb_streams;i++) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2326
                            if (c->switch_feed_streams[i] == pkt.stream_index)
2327
                                if (pkt.flags & AV_PKT_FLAG_KEY)
Reinhard Tartler's avatar
Reinhard Tartler committed
2328
                                    c->switch_feed_streams[i] = -1;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2329 2330
                            if (c->switch_feed_streams[i] >= 0)
                                c->switch_pending = 1;
2331
                        }
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2332 2333
                    }
                    for(i=0;i<c->stream->nb_streams;i++) {
2334
                        if (c->stream->feed_streams[i] == pkt.stream_index) {
2335
                            AVStream *st = c->fmt_in->streams[source_index];
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2336
                            pkt.stream_index = i;
2337
                            if (pkt.flags & AV_PKT_FLAG_KEY &&
2338
                                (st->codec->codec_type == AVMEDIA_TYPE_VIDEO ||
2339
                                 c->stream->nb_streams == 1))
2340 2341
                                c->got_key_frame = 1;
                            if (!c->stream->send_on_key || c->got_key_frame)
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2342 2343 2344 2345 2346
                                goto send_it;
                        }
                    }
                } else {
                    AVCodecContext *codec;
2347 2348 2349
                    AVStream *ist, *ost;
                send_it:
                    ist = c->fmt_in->streams[source_index];
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2350 2351 2352 2353 2354
                    /* specific handling for RTP: we use several
                       output stream (one for each RTP
                       connection). XXX: need more abstract handling */
                    if (c->is_packetized) {
                        /* compute send time and duration */
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2355
                        c->cur_pts = av_rescale_q(pkt.dts, ist->time_base, AV_TIME_BASE_Q);
2356
                        c->cur_pts -= c->first_pts;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2357
                        c->cur_frame_duration = av_rescale_q(pkt.duration, ist->time_base, AV_TIME_BASE_Q);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2358 2359 2360 2361
                        /* find RTP context */
                        c->packet_stream_index = pkt.stream_index;
                        ctx = c->rtp_ctx[c->packet_stream_index];
                        if(!ctx) {
2362
                            av_free_packet(&pkt);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2363
                            break;
2364
                        }
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2365 2366 2367 2368 2369 2370
                        codec = ctx->streams[0]->codec;
                        /* only one stream per RTP connection */
                        pkt.stream_index = 0;
                    } else {
                        ctx = &c->fmt_ctx;
                        /* Fudge here */
2371
                        codec = ctx->streams[pkt.stream_index]->codec;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2372 2373 2374 2375
                    }

                    if (c->is_packetized) {
                        int max_packet_size;
2376
                        if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP)
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2377 2378
                            max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
                        else
2379
                            max_packet_size = c->rtp_handles[c->packet_stream_index]->max_packet_size;
2380
                        ret = ffio_open_dyn_packet_buf(&ctx->pb, max_packet_size);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2381
                    } else {
2382
                        ret = avio_open_dyn_buf(&ctx->pb);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2383 2384 2385 2386 2387
                    }
                    if (ret < 0) {
                        /* XXX: potential leak */
                        return -1;
                    }
2388 2389
                    ost = ctx->streams[pkt.stream_index];

2390
                    ctx->pb->seekable = 0;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2391
                    if (pkt.dts != AV_NOPTS_VALUE)
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2392
                        pkt.dts = av_rescale_q(pkt.dts, ist->time_base, ost->time_base);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2393
                    if (pkt.pts != AV_NOPTS_VALUE)
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2394 2395
                        pkt.pts = av_rescale_q(pkt.pts, ist->time_base, ost->time_base);
                    pkt.duration = av_rescale_q(pkt.duration, ist->time_base, ost->time_base);
2396 2397
                    if (av_write_frame(ctx, &pkt) < 0) {
                        http_log("Error writing frame to output\n");
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2398
                        c->state = HTTPSTATE_SEND_DATA_TRAILER;
2399
                    }
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2400

2401
                    len = avio_close_dyn_buf(ctx->pb, &c->pb_buffer);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2402 2403 2404 2405 2406 2407 2408 2409
                    c->cur_frame_bytes = len;
                    c->buffer_ptr = c->pb_buffer;
                    c->buffer_end = c->pb_buffer + len;

                    codec->frame_number++;
                    if (len == 0) {
                        av_free_packet(&pkt);
                        goto redo;
2410
                    }
Fabrice Bellard's avatar
Fabrice Bellard committed
2411
                }
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2412
                av_free_packet(&pkt);
Fabrice Bellard's avatar
Fabrice Bellard committed
2413
            }
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2414
        }
Fabrice Bellard's avatar
Fabrice Bellard committed
2415 2416 2417 2418
        break;
    default:
    case HTTPSTATE_SEND_DATA_TRAILER:
        /* last packet test ? */
2419
        if (c->last_packet_sent || c->is_packetized)
Fabrice Bellard's avatar
Fabrice Bellard committed
2420
            return -1;
2421
        ctx = &c->fmt_ctx;
Fabrice Bellard's avatar
Fabrice Bellard committed
2422
        /* prepare header */
2423
        if (avio_open_dyn_buf(&ctx->pb) < 0) {
2424 2425 2426
            /* XXX: potential leak */
            return -1;
        }
2427
        c->fmt_ctx.pb->seekable = 0;
2428
        av_write_trailer(ctx);
2429
        len = avio_close_dyn_buf(ctx->pb, &c->pb_buffer);
2430 2431 2432
        c->buffer_ptr = c->pb_buffer;
        c->buffer_end = c->pb_buffer + len;

Fabrice Bellard's avatar
Fabrice Bellard committed
2433 2434 2435 2436 2437 2438 2439
        c->last_packet_sent = 1;
        break;
    }
    return 0;
}

/* should convert the format at the same time */
2440 2441
/* send data starting at c->buffer_ptr to the output connection
   (either UDP or TCP connection) */
2442
static int http_send_data(HTTPContext *c)
Fabrice Bellard's avatar
Fabrice Bellard committed
2443
{
2444
    int len, ret;
Fabrice Bellard's avatar
Fabrice Bellard committed
2445

2446 2447 2448 2449 2450
    for(;;) {
        if (c->buffer_ptr >= c->buffer_end) {
            ret = http_prepare_data(c);
            if (ret < 0)
                return -1;
2451
            else if (ret != 0)
2452 2453
                /* state change requested */
                break;
2454
        } else {
2455 2456 2457 2458 2459 2460 2461
            if (c->is_packetized) {
                /* RTP data output */
                len = c->buffer_end - c->buffer_ptr;
                if (len < 4) {
                    /* fail safe - should never happen */
                fail1:
                    c->buffer_ptr = c->buffer_end;
2462 2463
                    return 0;
                }
2464 2465 2466 2467 2468 2469
                len = (c->buffer_ptr[0] << 24) |
                    (c->buffer_ptr[1] << 16) |
                    (c->buffer_ptr[2] << 8) |
                    (c->buffer_ptr[3]);
                if (len > (c->buffer_end - c->buffer_ptr))
                    goto fail1;
2470 2471 2472 2473 2474 2475 2476 2477 2478 2479
                if ((get_packet_send_clock(c) - get_server_clock(c)) > 0) {
                    /* nothing to send yet: we can wait */
                    return 0;
                }

                c->data_count += len;
                update_datarate(&c->datarate, c->data_count);
                if (c->stream)
                    c->stream->bytes_served += len;

2480
                if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP) {
2481
                    /* RTP packets are sent inside the RTSP TCP connection */
2482
                    AVIOContext *pb;
2483 2484 2485
                    int interleaved_index, size;
                    uint8_t header[4];
                    HTTPContext *rtsp_c;
2486

2487 2488 2489 2490 2491
                    rtsp_c = c->rtsp_c;
                    /* if no RTSP connection left, error */
                    if (!rtsp_c)
                        return -1;
                    /* if already sending something, then wait. */
2492
                    if (rtsp_c->state != RTSPSTATE_WAIT_REQUEST)
2493
                        break;
2494
                    if (avio_open_dyn_buf(&pb) < 0)
2495 2496 2497 2498 2499 2500 2501 2502 2503 2504
                        goto fail1;
                    interleaved_index = c->packet_stream_index * 2;
                    /* RTCP packets are sent at odd indexes */
                    if (c->buffer_ptr[1] == 200)
                        interleaved_index++;
                    /* write RTSP TCP header */
                    header[0] = '$';
                    header[1] = interleaved_index;
                    header[2] = len >> 8;
                    header[3] = len;
2505
                    avio_write(pb, header, 4);
2506 2507
                    /* write RTP packet data */
                    c->buffer_ptr += 4;
2508
                    avio_write(pb, c->buffer_ptr, len);
2509
                    size = avio_close_dyn_buf(pb, &c->packet_buffer);
2510 2511 2512
                    /* prepare asynchronous TCP sending */
                    rtsp_c->packet_buffer_ptr = c->packet_buffer;
                    rtsp_c->packet_buffer_end = c->packet_buffer + size;
2513
                    c->buffer_ptr += len;
2514

2515
                    /* send everything we can NOW */
2516 2517
                    len = send(rtsp_c->fd, rtsp_c->packet_buffer_ptr,
                                rtsp_c->packet_buffer_end - rtsp_c->packet_buffer_ptr, 0);
2518
                    if (len > 0)
2519 2520 2521 2522 2523 2524 2525
                        rtsp_c->packet_buffer_ptr += len;
                    if (rtsp_c->packet_buffer_ptr < rtsp_c->packet_buffer_end) {
                        /* if we could not send all the data, we will
                           send it later, so a new state is needed to
                           "lock" the RTSP TCP connection */
                        rtsp_c->state = RTSPSTATE_SEND_PACKET;
                        break;
2526
                    } else
2527 2528 2529 2530
                        /* all data has been sent */
                        av_freep(&c->packet_buffer);
                } else {
                    /* send RTP packet directly in UDP */
2531
                    c->buffer_ptr += 4;
2532 2533
                    ffurl_write(c->rtp_handles[c->packet_stream_index],
                                c->buffer_ptr, len);
2534 2535
                    c->buffer_ptr += len;
                    /* here we continue as we can send several packets per 10 ms slot */
2536 2537 2538
                }
            } else {
                /* TCP data output */
2539
                len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2540
                if (len < 0) {
2541 2542
                    if (ff_neterrno() != AVERROR(EAGAIN) &&
                        ff_neterrno() != AVERROR(EINTR))
2543 2544
                        /* error : close connection */
                        return -1;
2545
                    else
2546
                        return 0;
2547
                } else
2548
                    c->buffer_ptr += len;
2549

2550 2551 2552 2553 2554
                c->data_count += len;
                update_datarate(&c->datarate, c->data_count);
                if (c->stream)
                    c->stream->bytes_served += len;
                break;
2555
            }
Fabrice Bellard's avatar
Fabrice Bellard committed
2556
        }
2557
    } /* for(;;) */
Fabrice Bellard's avatar
Fabrice Bellard committed
2558 2559 2560 2561 2562 2563 2564 2565 2566 2567
    return 0;
}

static int http_start_receive_data(HTTPContext *c)
{
    int fd;

    if (c->stream->feed_opened)
        return -1;

2568 2569 2570 2571
    /* Don't permit writing to this one */
    if (c->stream->readonly)
        return -1;

Fabrice Bellard's avatar
Fabrice Bellard committed
2572 2573
    /* open feed */
    fd = open(c->stream->feed_filename, O_RDWR);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2574 2575
    if (fd < 0) {
        http_log("Error opening feeder file: %s\n", strerror(errno));
Fabrice Bellard's avatar
Fabrice Bellard committed
2576
        return -1;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2577
    }
Fabrice Bellard's avatar
Fabrice Bellard committed
2578
    c->feed_fd = fd;
2579

2580 2581 2582 2583 2584 2585
    if (c->stream->truncate) {
        /* truncate feed file */
        ffm_write_write_index(c->feed_fd, FFM_PACKET_SIZE);
        ftruncate(c->feed_fd, FFM_PACKET_SIZE);
        http_log("Truncating feed file '%s'\n", c->stream->feed_filename);
    } else {
2586 2587 2588 2589
        if ((c->stream->feed_write_index = ffm_read_write_index(fd)) < 0) {
            http_log("Error reading write index from feed file: %s\n", strerror(errno));
            return -1;
        }
2590 2591
    }

2592
    c->stream->feed_write_index = FFMAX(ffm_read_write_index(fd), FFM_PACKET_SIZE);
Fabrice Bellard's avatar
Fabrice Bellard committed
2593 2594 2595 2596 2597 2598 2599
    c->stream->feed_size = lseek(fd, 0, SEEK_END);
    lseek(fd, 0, SEEK_SET);

    /* init buffer input */
    c->buffer_ptr = c->buffer;
    c->buffer_end = c->buffer + FFM_PACKET_SIZE;
    c->stream->feed_opened = 1;
2600
    c->chunked_encoding = !!av_stristr(c->buffer, "Transfer-Encoding: chunked");
Fabrice Bellard's avatar
Fabrice Bellard committed
2601 2602
    return 0;
}
2603

Fabrice Bellard's avatar
Fabrice Bellard committed
2604 2605 2606
static int http_receive_data(HTTPContext *c)
{
    HTTPContext *c1;
2607
    int len, loop_run = 0;
Fabrice Bellard's avatar
Fabrice Bellard committed
2608

2609 2610 2611 2612 2613 2614
    while (c->chunked_encoding && !c->chunk_size &&
           c->buffer_end > c->buffer_ptr) {
        /* read chunk header, if present */
        len = recv(c->fd, c->buffer_ptr, 1, 0);

        if (len < 0) {
2615 2616
            if (ff_neterrno() != AVERROR(EAGAIN) &&
                ff_neterrno() != AVERROR(EINTR))
2617 2618
                /* error : close connection */
                goto fail;
2619
            return 0;
2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636
        } else if (len == 0) {
            /* end of connection : close it */
            goto fail;
        } else if (c->buffer_ptr - c->buffer >= 2 &&
                   !memcmp(c->buffer_ptr - 1, "\r\n", 2)) {
            c->chunk_size = strtol(c->buffer, 0, 16);
            if (c->chunk_size == 0) // end of stream
                goto fail;
            c->buffer_ptr = c->buffer;
            break;
        } else if (++loop_run > 10) {
            /* no chunk header, abort */
            goto fail;
        } else {
            c->buffer_ptr++;
        }
    }
2637

2638 2639 2640
    if (c->buffer_end > c->buffer_ptr) {
        len = recv(c->fd, c->buffer_ptr,
                   FFMIN(c->chunk_size, c->buffer_end - c->buffer_ptr), 0);
2641
        if (len < 0) {
2642 2643
            if (ff_neterrno() != AVERROR(EAGAIN) &&
                ff_neterrno() != AVERROR(EINTR))
2644 2645
                /* error : close connection */
                goto fail;
2646
        } else if (len == 0)
2647 2648
            /* end of connection : close it */
            goto fail;
2649
        else {
2650
            c->chunk_size -= len;
2651 2652
            c->buffer_ptr += len;
            c->data_count += len;
2653
            update_datarate(&c->datarate, c->data_count);
2654 2655 2656
        }
    }

2657 2658 2659 2660 2661 2662 2663 2664
    if (c->buffer_ptr - c->buffer >= 2 && c->data_count > FFM_PACKET_SIZE) {
        if (c->buffer[0] != 'f' ||
            c->buffer[1] != 'm') {
            http_log("Feed stream has become desynchronized -- disconnecting\n");
            goto fail;
        }
    }

Fabrice Bellard's avatar
Fabrice Bellard committed
2665
    if (c->buffer_ptr >= c->buffer_end) {
2666
        FFStream *feed = c->stream;
Fabrice Bellard's avatar
Fabrice Bellard committed
2667 2668 2669
        /* a packet has been received : write it in the store, except
           if header */
        if (c->data_count > FFM_PACKET_SIZE) {
2670

2671
            //            printf("writing pos=0x%"PRIx64" size=0x%"PRIx64"\n", feed->feed_write_index, feed->feed_size);
Fabrice Bellard's avatar
Fabrice Bellard committed
2672 2673
            /* XXX: use llseek or url_seek */
            lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2674 2675 2676 2677
            if (write(c->feed_fd, c->buffer, FFM_PACKET_SIZE) < 0) {
                http_log("Error writing to feed file: %s\n", strerror(errno));
                goto fail;
            }
2678

Fabrice Bellard's avatar
Fabrice Bellard committed
2679 2680 2681 2682 2683 2684
            feed->feed_write_index += FFM_PACKET_SIZE;
            /* update file size */
            if (feed->feed_write_index > c->stream->feed_size)
                feed->feed_size = feed->feed_write_index;

            /* handle wrap around if max file size reached */
2685
            if (c->stream->feed_max_size && feed->feed_write_index >= c->stream->feed_max_size)
Fabrice Bellard's avatar
Fabrice Bellard committed
2686 2687 2688
                feed->feed_write_index = FFM_PACKET_SIZE;

            /* write index */
2689 2690 2691 2692
            if (ffm_write_write_index(c->feed_fd, feed->feed_write_index) < 0) {
                http_log("Error writing index to feed file: %s\n", strerror(errno));
                goto fail;
            }
Fabrice Bellard's avatar
Fabrice Bellard committed
2693 2694 2695

            /* wake up any waiting connections */
            for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2696
                if (c1->state == HTTPSTATE_WAIT_FEED &&
2697
                    c1->stream->feed == c->stream->feed)
Fabrice Bellard's avatar
Fabrice Bellard committed
2698 2699
                    c1->state = HTTPSTATE_SEND_DATA;
            }
2700 2701
        } else {
            /* We have a header in our hands that contains useful data */
2702
            AVFormatContext *s = avformat_alloc_context();
2703
            AVIOContext *pb;
2704
            AVInputFormat *fmt_in;
2705 2706
            int i;

2707 2708 2709
            if (!s)
                goto fail;

2710 2711 2712 2713 2714
            /* use feed output format name to find corresponding input format */
            fmt_in = av_find_input_format(feed->fmt->name);
            if (!fmt_in)
                goto fail;

2715 2716
            pb = avio_alloc_context(c->buffer, c->buffer_end - c->buffer,
                                    0, NULL, NULL, NULL, NULL);
2717
            pb->seekable = 0;
2718

2719 2720
            s->pb = pb;
            if (avformat_open_input(&s, c->stream->feed_filename, fmt_in, NULL) < 0) {
2721 2722 2723
                av_free(pb);
                goto fail;
            }
2724 2725

            /* Now we have the actual streams */
2726
            if (s->nb_streams != feed->nb_streams) {
2727
                avformat_close_input(&s);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2728
                av_free(pb);
2729 2730
                http_log("Feed '%s' stream number does not match registered feed\n",
                         c->stream->feed_filename);
2731 2732
                goto fail;
            }
2733

2734 2735 2736
            for (i = 0; i < s->nb_streams; i++) {
                AVStream *fst = feed->streams[i];
                AVStream *st = s->streams[i];
2737
                avcodec_copy_context(fst->codec, st->codec);
2738
            }
2739

2740
            avformat_close_input(&s);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2741
            av_free(pb);
Fabrice Bellard's avatar
Fabrice Bellard committed
2742 2743 2744 2745 2746 2747 2748 2749
        }
        c->buffer_ptr = c->buffer;
    }

    return 0;
 fail:
    c->stream->feed_opened = 0;
    close(c->feed_fd);
2750 2751 2752 2753 2754 2755
    /* wake up any waiting connections to stop waiting for feed */
    for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
        if (c1->state == HTTPSTATE_WAIT_FEED &&
            c1->stream->feed == c->stream->feed)
            c1->state = HTTPSTATE_SEND_DATA_TRAILER;
    }
Fabrice Bellard's avatar
Fabrice Bellard committed
2756 2757 2758
    return -1;
}

2759 2760 2761 2762 2763 2764 2765
/********************************************************************/
/* RTSP handling */

static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
{
    const char *str;
    time_t ti;
2766
    struct tm *tm;
2767 2768 2769
    char buf2[32];

    switch(error_number) {
2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802
    case RTSP_STATUS_OK:
        str = "OK";
        break;
    case RTSP_STATUS_METHOD:
        str = "Method Not Allowed";
        break;
    case RTSP_STATUS_BANDWIDTH:
        str = "Not Enough Bandwidth";
        break;
    case RTSP_STATUS_SESSION:
        str = "Session Not Found";
        break;
    case RTSP_STATUS_STATE:
        str = "Method Not Valid in This State";
        break;
    case RTSP_STATUS_AGGREGATE:
        str = "Aggregate operation not allowed";
        break;
    case RTSP_STATUS_ONLY_AGGREGATE:
        str = "Only aggregate operation allowed";
        break;
    case RTSP_STATUS_TRANSPORT:
        str = "Unsupported transport";
        break;
    case RTSP_STATUS_INTERNAL:
        str = "Internal Server Error";
        break;
    case RTSP_STATUS_SERVICE:
        str = "Service Unavailable";
        break;
    case RTSP_STATUS_VERSION:
        str = "RTSP Version not supported";
        break;
2803 2804 2805 2806
    default:
        str = "Unknown Error";
        break;
    }
2807

2808 2809
    avio_printf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
    avio_printf(c->pb, "CSeq: %d\r\n", c->seq);
2810 2811 2812

    /* output GMT time */
    ti = time(NULL);
2813 2814
    tm = gmtime(&ti);
    strftime(buf2, sizeof(buf2), "%a, %d %b %Y %H:%M:%S", tm);
2815
    avio_printf(c->pb, "Date: %s GMT\r\n", buf2);
2816 2817 2818 2819 2820
}

static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
{
    rtsp_reply_header(c, error_number);
2821
    avio_printf(c->pb, "\r\n");
2822 2823 2824 2825 2826 2827 2828 2829 2830 2831
}

static int rtsp_parse_request(HTTPContext *c)
{
    const char *p, *p1, *p2;
    char cmd[32];
    char url[1024];
    char protocol[32];
    char line[1024];
    int len;
2832
    RTSPMessageHeader header1, *header = &header1;
2833

2834 2835
    c->buffer_ptr[0] = '\0';
    p = c->buffer;
2836

2837 2838 2839 2840
    get_word(cmd, sizeof(cmd), &p);
    get_word(url, sizeof(url), &p);
    get_word(protocol, sizeof(protocol), &p);

2841 2842 2843
    av_strlcpy(c->method, cmd, sizeof(c->method));
    av_strlcpy(c->url, url, sizeof(c->url));
    av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
2844

2845
    if (avio_open_dyn_buf(&c->pb) < 0) {
2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857
        /* XXX: cannot do more */
        c->pb = NULL; /* safety */
        return -1;
    }

    /* check version name */
    if (strcmp(protocol, "RTSP/1.0") != 0) {
        rtsp_reply_error(c, RTSP_STATUS_VERSION);
        goto the_end;
    }

    /* parse each header line */
2858
    memset(header, 0, sizeof(*header));
2859 2860 2861 2862 2863 2864
    /* skip to next line */
    while (*p != '\n' && *p != '\0')
        p++;
    if (*p == '\n')
        p++;
    while (*p != '\0') {
2865
        p1 = memchr(p, '\n', (char *)c->buffer_ptr - p);
2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878
        if (!p1)
            break;
        p2 = p1;
        if (p2 > p && p2[-1] == '\r')
            p2--;
        /* skip empty line */
        if (p2 == p)
            break;
        len = p2 - p;
        if (len > sizeof(line) - 1)
            len = sizeof(line) - 1;
        memcpy(line, p, len);
        line[len] = '\0';
2879
        ff_rtsp_parse_line(header, line, NULL, NULL);
2880 2881 2882 2883 2884 2885
        p = p1 + 1;
    }

    /* handle sequence number */
    c->seq = header->seq;

2886
    if (!strcmp(cmd, "DESCRIBE"))
2887
        rtsp_cmd_describe(c, url);
2888
    else if (!strcmp(cmd, "OPTIONS"))
2889
        rtsp_cmd_options(c, url);
2890
    else if (!strcmp(cmd, "SETUP"))
2891
        rtsp_cmd_setup(c, url, header);
2892
    else if (!strcmp(cmd, "PLAY"))
2893
        rtsp_cmd_play(c, url, header);
2894
    else if (!strcmp(cmd, "PAUSE"))
2895
        rtsp_cmd_pause(c, url, header);
2896
    else if (!strcmp(cmd, "TEARDOWN"))
2897
        rtsp_cmd_teardown(c, url, header);
2898
    else
2899
        rtsp_reply_error(c, RTSP_STATUS_METHOD);
2900

2901
 the_end:
2902
    len = avio_close_dyn_buf(c->pb, &c->pb_buffer);
2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913
    c->pb = NULL; /* safety */
    if (len < 0) {
        /* XXX: cannot do more */
        return -1;
    }
    c->buffer_ptr = c->pb_buffer;
    c->buffer_end = c->pb_buffer + len;
    c->state = RTSPSTATE_SEND_REPLY;
    return 0;
}

2914
static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
2915
                                   struct in_addr my_ip)
2916
{
2917
    AVFormatContext *avc;
2918
    AVStream *avs = NULL;
2919
    int i;
2920

2921
    avc =  avformat_alloc_context();
2922
    if (avc == NULL) {
2923
        return -1;
2924
    }
2925 2926
    av_dict_set(&avc->metadata, "title",
               stream->title[0] ? stream->title : "No Title", 0);
2927 2928 2929 2930 2931
    avc->nb_streams = stream->nb_streams;
    if (stream->is_multicast) {
        snprintf(avc->filename, 1024, "rtp://%s:%d?multicast=1?ttl=%d",
                 inet_ntoa(stream->multicast_ip),
                 stream->multicast_port, stream->multicast_ttl);
2932 2933
    } else {
        snprintf(avc->filename, 1024, "rtp://0.0.0.0");
2934
    }
2935

2936 2937 2938 2939 2940 2941 2942
    if (avc->nb_streams >= INT_MAX/sizeof(*avc->streams) ||
        !(avc->streams = av_malloc(avc->nb_streams * sizeof(*avc->streams))))
        goto sdp_done;
    if (avc->nb_streams >= INT_MAX/sizeof(*avs) ||
        !(avs = av_malloc(avc->nb_streams * sizeof(*avs))))
        goto sdp_done;

2943
    for(i = 0; i < stream->nb_streams; i++) {
2944 2945
        avc->streams[i] = &avs[i];
        avc->streams[i]->codec = stream->streams[i]->codec;
2946
    }
2947
    *pbuffer = av_mallocz(2048);
2948
    av_sdp_create(&avc, 1, *pbuffer, 2048);
2949 2950 2951

 sdp_done:
    av_free(avc->streams);
2952
    av_dict_free(&avc->metadata);
2953
    av_free(avc);
2954
    av_free(avs);
2955 2956

    return strlen(*pbuffer);
2957 2958
}

2959 2960 2961
static void rtsp_cmd_options(HTTPContext *c, const char *url)
{
//    rtsp_reply_header(c, RTSP_STATUS_OK);
2962 2963 2964 2965
    avio_printf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK");
    avio_printf(c->pb, "CSeq: %d\r\n", c->seq);
    avio_printf(c->pb, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
    avio_printf(c->pb, "\r\n");
2966 2967
}

2968 2969 2970 2971 2972
static void rtsp_cmd_describe(HTTPContext *c, const char *url)
{
    FFStream *stream;
    char path1[1024];
    const char *path;
2973
    uint8_t *content;
2974 2975
    int content_length, len;
    struct sockaddr_in my_addr;
2976

2977
    /* find which url is asked */
2978
    av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2979 2980 2981 2982 2983
    path = path1;
    if (*path == '/')
        path++;

    for(stream = first_stream; stream != NULL; stream = stream->next) {
2984 2985
        if (!stream->is_feed &&
            stream->fmt && !strcmp(stream->fmt->name, "rtp") &&
2986 2987 2988 2989 2990 2991 2992 2993 2994 2995
            !strcmp(path, stream->filename)) {
            goto found;
        }
    }
    /* no stream found */
    rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
    return;

 found:
    /* prepare the media description in sdp format */
2996 2997 2998 2999 3000

    /* get the host IP */
    len = sizeof(my_addr);
    getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
    content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
3001 3002 3003 3004 3005
    if (content_length < 0) {
        rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
        return;
    }
    rtsp_reply_header(c, RTSP_STATUS_OK);
3006 3007 3008 3009
    avio_printf(c->pb, "Content-Base: %s/\r\n", url);
    avio_printf(c->pb, "Content-Type: application/sdp\r\n");
    avio_printf(c->pb, "Content-Length: %d\r\n", content_length);
    avio_printf(c->pb, "\r\n");
3010
    avio_write(c->pb, content, content_length);
3011
    av_free(content);
3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027
}

static HTTPContext *find_rtp_session(const char *session_id)
{
    HTTPContext *c;

    if (session_id[0] == '\0')
        return NULL;

    for(c = first_http_ctx; c != NULL; c = c->next) {
        if (!strcmp(c->session_id, session_id))
            return c;
    }
    return NULL;
}

3028
static RTSPTransportField *find_transport(RTSPMessageHeader *h, enum RTSPLowerTransport lower_transport)
3029 3030 3031 3032 3033 3034
{
    RTSPTransportField *th;
    int i;

    for(i=0;i<h->nb_transports;i++) {
        th = &h->transports[i];
3035
        if (th->lower_transport == lower_transport)
3036 3037 3038 3039 3040
            return th;
    }
    return NULL;
}

3041
static void rtsp_cmd_setup(HTTPContext *c, const char *url,
3042
                           RTSPMessageHeader *h)
3043 3044
{
    FFStream *stream;
3045
    int stream_index, rtp_port, rtcp_port;
3046 3047 3048 3049 3050 3051 3052
    char buf[1024];
    char path1[1024];
    const char *path;
    HTTPContext *rtp_c;
    RTSPTransportField *th;
    struct sockaddr_in dest_addr;
    RTSPActionServerSetup setup;
3053

3054
    /* find which url is asked */
3055
    av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
3056 3057 3058 3059 3060 3061
    path = path1;
    if (*path == '/')
        path++;

    /* now check each stream */
    for(stream = first_stream; stream != NULL; stream = stream->next) {
3062 3063
        if (!stream->is_feed &&
            stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
3064 3065 3066 3067 3068 3069 3070 3071 3072
            /* accept aggregate filenames only if single stream */
            if (!strcmp(path, stream->filename)) {
                if (stream->nb_streams != 1) {
                    rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
                    return;
                }
                stream_index = 0;
                goto found;
            }
3073

3074 3075
            for(stream_index = 0; stream_index < stream->nb_streams;
                stream_index++) {
3076
                snprintf(buf, sizeof(buf), "%s/streamid=%d",
3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088
                         stream->filename, stream_index);
                if (!strcmp(path, buf))
                    goto found;
            }
        }
    }
    /* no stream found */
    rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
    return;
 found:

    /* generate session id if needed */
3089
    if (h->session_id[0] == '\0')
3090
        snprintf(h->session_id, sizeof(h->session_id), "%08x%08x",
3091
                 av_lfg_get(&random_state), av_lfg_get(&random_state));
3092 3093 3094 3095

    /* find rtp session, and create it if none found */
    rtp_c = find_rtp_session(h->session_id);
    if (!rtp_c) {
3096
        /* always prefer UDP */
3097
        th = find_transport(h, RTSP_LOWER_TRANSPORT_UDP);
3098
        if (!th) {
3099
            th = find_transport(h, RTSP_LOWER_TRANSPORT_TCP);
3100 3101 3102 3103 3104 3105 3106
            if (!th) {
                rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
                return;
            }
        }

        rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id,
3107
                                   th->lower_transport);
3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118
        if (!rtp_c) {
            rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
            return;
        }

        /* open input stream */
        if (open_input_stream(rtp_c, "") < 0) {
            rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
            return;
        }
    }
3119

3120 3121 3122 3123 3124 3125
    /* test if stream is OK (test needed because several SETUP needs
       to be done for a given file) */
    if (rtp_c->stream != stream) {
        rtsp_reply_error(c, RTSP_STATUS_SERVICE);
        return;
    }
3126

3127 3128 3129 3130 3131 3132 3133 3134
    /* test if stream is already set up */
    if (rtp_c->rtp_ctx[stream_index]) {
        rtsp_reply_error(c, RTSP_STATUS_STATE);
        return;
    }

    /* check transport */
    th = find_transport(h, rtp_c->rtp_protocol);
3135
    if (!th || (th->lower_transport == RTSP_LOWER_TRANSPORT_UDP &&
3136 3137 3138 3139 3140 3141 3142 3143 3144
                th->client_port_min <= 0)) {
        rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
        return;
    }

    /* setup default options */
    setup.transport_option[0] = '\0';
    dest_addr = rtp_c->from_addr;
    dest_addr.sin_port = htons(th->client_port_min);
3145

3146
    /* setup stream */
3147
    if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) {
3148 3149 3150 3151 3152 3153 3154
        rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
        return;
    }

    /* now everything is OK, so we can send the connection parameters */
    rtsp_reply_header(c, RTSP_STATUS_OK);
    /* session ID */
3155
    avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3156 3157

    switch(rtp_c->rtp_protocol) {
3158
    case RTSP_LOWER_TRANSPORT_UDP:
3159 3160
        rtp_port = ff_rtp_get_local_rtp_port(rtp_c->rtp_handles[stream_index]);
        rtcp_port = ff_rtp_get_local_rtcp_port(rtp_c->rtp_handles[stream_index]);
3161
        avio_printf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
3162
                    "client_port=%d-%d;server_port=%d-%d",
3163 3164
                    th->client_port_min, th->client_port_max,
                    rtp_port, rtcp_port);
3165
        break;
3166
    case RTSP_LOWER_TRANSPORT_TCP:
3167
        avio_printf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
3168 3169 3170 3171 3172
                    stream_index * 2, stream_index * 2 + 1);
        break;
    default:
        break;
    }
3173
    if (setup.transport_option[0] != '\0')
3174 3175
        avio_printf(c->pb, ";%s", setup.transport_option);
    avio_printf(c->pb, "\r\n");
3176

3177

3178
    avio_printf(c->pb, "\r\n");
3179 3180 3181 3182 3183
}


/* find an rtp connection by using the session ID. Check consistency
   with filename */
3184
static HTTPContext *find_rtp_session_with_url(const char *url,
3185 3186 3187 3188 3189
                                              const char *session_id)
{
    HTTPContext *rtp_c;
    char path1[1024];
    const char *path;
3190
    char buf[1024];
3191
    int s, len;
3192 3193 3194 3195 3196 3197

    rtp_c = find_rtp_session(session_id);
    if (!rtp_c)
        return NULL;

    /* find which url is asked */
3198
    av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
3199 3200 3201
    path = path1;
    if (*path == '/')
        path++;
3202 3203 3204 3205 3206 3207 3208 3209 3210
    if(!strcmp(path, rtp_c->stream->filename)) return rtp_c;
    for(s=0; s<rtp_c->stream->nb_streams; ++s) {
      snprintf(buf, sizeof(buf), "%s/streamid=%d",
        rtp_c->stream->filename, s);
      if(!strncmp(path, buf, sizeof(buf))) {
    // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
        return rtp_c;
      }
    }
3211 3212 3213 3214
    len = strlen(path);
    if (len > 0 && path[len - 1] == '/' &&
        !strncmp(path, rtp_c->stream->filename, len - 1))
        return rtp_c;
3215
    return NULL;
3216 3217
}

3218
static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPMessageHeader *h)
3219 3220 3221 3222 3223 3224 3225 3226
{
    HTTPContext *rtp_c;

    rtp_c = find_rtp_session_with_url(url, h->session_id);
    if (!rtp_c) {
        rtsp_reply_error(c, RTSP_STATUS_SESSION);
        return;
    }
3227

3228 3229 3230 3231 3232 3233 3234 3235
    if (rtp_c->state != HTTPSTATE_SEND_DATA &&
        rtp_c->state != HTTPSTATE_WAIT_FEED &&
        rtp_c->state != HTTPSTATE_READY) {
        rtsp_reply_error(c, RTSP_STATUS_STATE);
        return;
    }

    rtp_c->state = HTTPSTATE_SEND_DATA;
3236

3237 3238 3239
    /* now everything is OK, so we can send the connection parameters */
    rtsp_reply_header(c, RTSP_STATUS_OK);
    /* session ID */
3240 3241
    avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
    avio_printf(c->pb, "\r\n");
3242 3243
}

3244
static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPMessageHeader *h)
3245 3246 3247 3248 3249 3250 3251 3252
{
    HTTPContext *rtp_c;

    rtp_c = find_rtp_session_with_url(url, h->session_id);
    if (!rtp_c) {
        rtsp_reply_error(c, RTSP_STATUS_SESSION);
        return;
    }
3253

3254 3255 3256 3257 3258
    if (rtp_c->state != HTTPSTATE_SEND_DATA &&
        rtp_c->state != HTTPSTATE_WAIT_FEED) {
        rtsp_reply_error(c, RTSP_STATUS_STATE);
        return;
    }
3259

3260
    rtp_c->state = HTTPSTATE_READY;
3261
    rtp_c->first_pts = AV_NOPTS_VALUE;
3262 3263 3264
    /* now everything is OK, so we can send the connection parameters */
    rtsp_reply_header(c, RTSP_STATUS_OK);
    /* session ID */
3265 3266
    avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
    avio_printf(c->pb, "\r\n");
3267 3268
}

3269
static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPMessageHeader *h)
3270 3271 3272 3273 3274 3275 3276 3277
{
    HTTPContext *rtp_c;

    rtp_c = find_rtp_session_with_url(url, h->session_id);
    if (!rtp_c) {
        rtsp_reply_error(c, RTSP_STATUS_SESSION);
        return;
    }
3278

3279 3280 3281
    /* now everything is OK, so we can send the connection parameters */
    rtsp_reply_header(c, RTSP_STATUS_OK);
    /* session ID */
3282
    avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3283
    avio_printf(c->pb, "\r\n");
3284 3285 3286

    /* abort the session */
    close_connection(rtp_c);
3287 3288 3289 3290 3291 3292
}


/********************************************************************/
/* RTP handling */

3293
static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
3294
                                       FFStream *stream, const char *session_id,
3295
                                       enum RTSPLowerTransport rtp_protocol)
3296 3297
{
    HTTPContext *c = NULL;
3298
    const char *proto_str;
3299

3300 3301 3302 3303
    /* XXX: should output a warning page when coming
       close to the connection limit */
    if (nb_connections >= nb_max_connections)
        goto fail;
3304

3305 3306 3307 3308
    /* add a new connection */
    c = av_mallocz(sizeof(HTTPContext));
    if (!c)
        goto fail;
3309

3310 3311
    c->fd = -1;
    c->poll_entry = NULL;
3312
    c->from_addr = *from_addr;
3313 3314 3315 3316 3317 3318
    c->buffer_size = IOBUFFER_INIT_SIZE;
    c->buffer = av_malloc(c->buffer_size);
    if (!c->buffer)
        goto fail;
    nb_connections++;
    c->stream = stream;
3319
    av_strlcpy(c->session_id, session_id, sizeof(c->session_id));
3320 3321
    c->state = HTTPSTATE_READY;
    c->is_packetized = 1;
3322 3323
    c->rtp_protocol = rtp_protocol;

3324
    /* protocol is shown in statistics */
3325
    switch(c->rtp_protocol) {
3326
    case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
3327 3328
        proto_str = "MCAST";
        break;
3329
    case RTSP_LOWER_TRANSPORT_UDP:
3330 3331
        proto_str = "UDP";
        break;
3332
    case RTSP_LOWER_TRANSPORT_TCP:
3333 3334 3335 3336 3337 3338
        proto_str = "TCP";
        break;
    default:
        proto_str = "???";
        break;
    }
3339 3340
    av_strlcpy(c->protocol, "RTP/", sizeof(c->protocol));
    av_strlcat(c->protocol, proto_str, sizeof(c->protocol));
3341

3342 3343
    current_bandwidth += stream->bandwidth;

3344 3345 3346
    c->next = first_http_ctx;
    first_http_ctx = c;
    return c;
3347

3348 3349 3350 3351 3352 3353 3354 3355 3356
 fail:
    if (c) {
        av_free(c->buffer);
        av_free(c);
    }
    return NULL;
}

/* add a new RTP stream in an RTP connection (used in RTSP SETUP
3357
   command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
3358
   used. */
3359
static int rtp_new_av_stream(HTTPContext *c,
3360 3361
                             int stream_index, struct sockaddr_in *dest_addr,
                             HTTPContext *rtsp_c)
3362 3363 3364 3365
{
    AVFormatContext *ctx;
    AVStream *st;
    char *ipaddr;
3366
    URLContext *h = NULL;
3367
    uint8_t *dummy_buf;
3368
    int max_packet_size;
3369

3370
    /* now we can open the relevant output stream */
3371
    ctx = avformat_alloc_context();
3372 3373
    if (!ctx)
        return -1;
3374
    ctx->oformat = av_guess_format("rtp", NULL, NULL);
3375 3376 3377 3378 3379

    st = av_mallocz(sizeof(AVStream));
    if (!st)
        goto fail;
    ctx->nb_streams = 1;
3380
    ctx->streams = av_mallocz(sizeof(AVStream *) * ctx->nb_streams);
Mike William's avatar
Mike William committed
3381
    if (!ctx->streams)
3382
      goto fail;
3383 3384
    ctx->streams[0] = st;

3385
    if (!c->stream->feed ||
3386
        c->stream->feed == c->stream)
3387
        memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3388
    else
3389
        memcpy(st,
3390 3391
               c->stream->feed->streams[c->stream->feed_streams[stream_index]],
               sizeof(AVStream));
3392
    st->priv_data = NULL;
3393

3394 3395 3396 3397
    /* build destination RTP address */
    ipaddr = inet_ntoa(dest_addr->sin_addr);

    switch(c->rtp_protocol) {
3398 3399
    case RTSP_LOWER_TRANSPORT_UDP:
    case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
3400
        /* RTP/UDP case */
3401

3402 3403 3404 3405 3406 3407 3408
        /* XXX: also pass as parameter to function ? */
        if (c->stream->is_multicast) {
            int ttl;
            ttl = c->stream->multicast_ttl;
            if (!ttl)
                ttl = 16;
            snprintf(ctx->filename, sizeof(ctx->filename),
3409
                     "rtp://%s:%d?multicast=1&ttl=%d",
3410 3411 3412 3413 3414
                     ipaddr, ntohs(dest_addr->sin_port), ttl);
        } else {
            snprintf(ctx->filename, sizeof(ctx->filename),
                     "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
        }
3415

3416
        if (ffurl_open(&h, ctx->filename, AVIO_FLAG_WRITE, NULL, NULL) < 0)
3417 3418
            goto fail;
        c->rtp_handles[stream_index] = h;
3419
        max_packet_size = h->max_packet_size;
3420
        break;
3421
    case RTSP_LOWER_TRANSPORT_TCP:
3422 3423 3424 3425 3426
        /* RTP/TCP case */
        c->rtsp_c = rtsp_c;
        max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
        break;
    default:
3427 3428 3429
        goto fail;
    }

3430
    http_log("%s:%d - - \"PLAY %s/streamid=%d %s\"\n",
3431
             ipaddr, ntohs(dest_addr->sin_port),
3432
             c->stream->filename, stream_index, c->protocol);
3433

3434
    /* normally, no packets should be output here, but the packet size may be checked */
3435
    if (ffio_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) {
3436 3437 3438
        /* XXX: close stream */
        goto fail;
    }
3439
    if (avformat_write_header(ctx, NULL) < 0) {
3440 3441
    fail:
        if (h)
3442
            ffurl_close(h);
3443 3444 3445
        av_free(ctx);
        return -1;
    }
3446
    avio_close_dyn_buf(ctx->pb, &dummy_buf);
3447
    av_free(dummy_buf);
3448

3449 3450 3451 3452 3453 3454 3455
    c->rtp_ctx[stream_index] = ctx;
    return 0;
}

/********************************************************************/
/* ffserver initialization */

3456
static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec, int copy)
3457 3458 3459 3460 3461 3462
{
    AVStream *fst;

    fst = av_mallocz(sizeof(AVStream));
    if (!fst)
        return NULL;
3463
    if (copy) {
3464
        fst->codec = avcodec_alloc_context3(NULL);
3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476
        memcpy(fst->codec, codec, sizeof(AVCodecContext));
        if (codec->extradata_size) {
            fst->codec->extradata = av_malloc(codec->extradata_size);
            memcpy(fst->codec->extradata, codec->extradata,
                codec->extradata_size);
        }
    } else {
        /* live streams must use the actual feed's codec since it may be
         * updated later to carry extradata needed by the streams.
         */
        fst->codec = codec;
    }
3477
    fst->priv_data = av_mallocz(sizeof(FeedData));
3478
    fst->index = stream->nb_streams;
3479
    avpriv_set_pts_info(fst, 33, 1, 90000);
3480
    fst->sample_aspect_ratio = codec->sample_aspect_ratio;
3481 3482 3483 3484
    stream->streams[stream->nb_streams++] = fst;
    return fst;
}

Fabrice Bellard's avatar
Fabrice Bellard committed
3485
/* return the stream number in the feed */
3486
static int add_av_stream(FFStream *feed, AVStream *st)
Fabrice Bellard's avatar
Fabrice Bellard committed
3487 3488 3489 3490 3491
{
    AVStream *fst;
    AVCodecContext *av, *av1;
    int i;

3492
    av = st->codec;
Fabrice Bellard's avatar
Fabrice Bellard committed
3493 3494
    for(i=0;i<feed->nb_streams;i++) {
        st = feed->streams[i];
3495
        av1 = st->codec;
3496 3497
        if (av1->codec_id == av->codec_id &&
            av1->codec_type == av->codec_type &&
Fabrice Bellard's avatar
Fabrice Bellard committed
3498 3499 3500
            av1->bit_rate == av->bit_rate) {

            switch(av->codec_type) {
3501
            case AVMEDIA_TYPE_AUDIO:
Fabrice Bellard's avatar
Fabrice Bellard committed
3502 3503
                if (av1->channels == av->channels &&
                    av1->sample_rate == av->sample_rate)
3504
                    return i;
Fabrice Bellard's avatar
Fabrice Bellard committed
3505
                break;
3506
            case AVMEDIA_TYPE_VIDEO:
Fabrice Bellard's avatar
Fabrice Bellard committed
3507 3508
                if (av1->width == av->width &&
                    av1->height == av->height &&
3509 3510
                    av1->time_base.den == av->time_base.den &&
                    av1->time_base.num == av->time_base.num &&
Fabrice Bellard's avatar
Fabrice Bellard committed
3511
                    av1->gop_size == av->gop_size)
3512
                    return i;
Fabrice Bellard's avatar
Fabrice Bellard committed
3513
                break;
3514
            default:
3515
                abort();
Fabrice Bellard's avatar
Fabrice Bellard committed
3516 3517 3518
            }
        }
    }
3519

3520
    fst = add_av_stream1(feed, av, 0);
Fabrice Bellard's avatar
Fabrice Bellard committed
3521 3522 3523 3524 3525
    if (!fst)
        return -1;
    return feed->nb_streams - 1;
}

3526
static void remove_stream(FFStream *stream)
3527 3528 3529 3530
{
    FFStream **ps;
    ps = &first_stream;
    while (*ps != NULL) {
3531
        if (*ps == stream)
3532
            *ps = (*ps)->next;
3533
        else
3534 3535 3536 3537
            ps = &(*ps)->next;
    }
}

3538
/* specific mpeg4 handling : we extract the raw parameters */
3539
static void extract_mpeg4_header(AVFormatContext *infile)
3540 3541 3542 3543
{
    int mpeg4_count, i, size;
    AVPacket pkt;
    AVStream *st;
3544
    const uint8_t *p;
3545 3546 3547 3548

    mpeg4_count = 0;
    for(i=0;i<infile->nb_streams;i++) {
        st = infile->streams[i];
3549 3550
        if (st->codec->codec_id == CODEC_ID_MPEG4 &&
            st->codec->extradata_size == 0) {
3551 3552 3553 3554 3555 3556
            mpeg4_count++;
        }
    }
    if (!mpeg4_count)
        return;

3557
    printf("MPEG4 without extra data: trying to find header in %s\n", infile->filename);
3558 3559 3560 3561
    while (mpeg4_count > 0) {
        if (av_read_packet(infile, &pkt) < 0)
            break;
        st = infile->streams[pkt.stream_index];
3562 3563 3564
        if (st->codec->codec_id == CODEC_ID_MPEG4 &&
            st->codec->extradata_size == 0) {
            av_freep(&st->codec->extradata);
3565 3566 3567 3568 3569
            /* fill extradata with the header */
            /* XXX: we make hard suppositions here ! */
            p = pkt.data;
            while (p < pkt.data + pkt.size - 4) {
                /* stop when vop header is found */
3570
                if (p[0] == 0x00 && p[1] == 0x00 &&
3571 3572
                    p[2] == 0x01 && p[3] == 0xb6) {
                    size = p - pkt.data;
3573
                    //                    av_hex_dump_log(infile, AV_LOG_DEBUG, pkt.data, size);
3574 3575 3576
                    st->codec->extradata = av_malloc(size);
                    st->codec->extradata_size = size;
                    memcpy(st->codec->extradata, pkt.data, size);
3577 3578 3579 3580 3581 3582 3583 3584 3585 3586
                    break;
                }
                p++;
            }
            mpeg4_count--;
        }
        av_free_packet(&pkt);
    }
}

3587
/* compute the needed AVStream for each file */
3588
static void build_file_streams(void)
3589 3590
{
    FFStream *stream, *stream_next;
3591
    int i, ret;
3592 3593 3594

    /* gather all streams */
    for(stream = first_stream; stream != NULL; stream = stream_next) {
3595
        AVFormatContext *infile = NULL;
3596 3597 3598 3599 3600 3601
        stream_next = stream->next;
        if (stream->stream_type == STREAM_TYPE_LIVE &&
            !stream->feed) {
            /* the stream comes from a file */
            /* try to open the file */
            /* open stream */
3602
            if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
3603 3604
                /* specific case : if transport stream output to RTP,
                   we use a raw transport stream reader */
3605
                av_dict_set(&stream->in_opts, "mpeg2ts_compute_pcr", "1", 0);
3606
            }
3607

3608
            http_log("Opening file '%s'\n", stream->feed_filename);
3609
            if ((ret = avformat_open_input(&infile, stream->feed_filename, stream->ifmt, &stream->in_opts)) < 0) {
3610
                http_log("Could not open '%s': %d\n", stream->feed_filename, ret);
3611 3612 3613 3614 3615 3616
                /* remove stream (no need to spend more time on it) */
            fail:
                remove_stream(stream);
            } else {
                /* find all the AVStreams inside and reference them in
                   'stream' */
3617
                if (avformat_find_stream_info(infile, NULL) < 0) {
3618
                    http_log("Could not find codec parameters from '%s'\n",
3619
                             stream->feed_filename);
3620
                    avformat_close_input(&infile);
3621 3622
                    goto fail;
                }
3623 3624
                extract_mpeg4_header(infile);

3625
                for(i=0;i<infile->nb_streams;i++)
3626
                    add_av_stream1(stream, infile->streams[i]->codec, 1);
3627

3628
                avformat_close_input(&infile);
3629 3630 3631 3632 3633
            }
        }
    }
}

Fabrice Bellard's avatar
Fabrice Bellard committed
3634
/* compute the needed AVStream for each feed */
3635
static void build_feed_streams(void)
Fabrice Bellard's avatar
Fabrice Bellard committed
3636 3637 3638 3639
{
    FFStream *stream, *feed;
    int i;

3640 3641 3642 3643 3644
    /* gather all streams */
    for(stream = first_stream; stream != NULL; stream = stream->next) {
        feed = stream->feed;
        if (feed) {
            if (stream->is_feed) {
3645
                for(i=0;i<stream->nb_streams;i++)
Fabrice Bellard's avatar
Fabrice Bellard committed
3646
                    stream->feed_streams[i] = i;
3647 3648 3649 3650
            } else {
                /* we handle a stream coming from a feed */
                for(i=0;i<stream->nb_streams;i++)
                    stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
Fabrice Bellard's avatar
Fabrice Bellard committed
3651 3652 3653 3654 3655 3656 3657 3658
            }
        }
    }

    /* create feed files if needed */
    for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
        int fd;

3659
        if (avio_check(feed->feed_filename, AVIO_FLAG_READ) > 0) {
3660
            /* See if it matches */
3661
            AVFormatContext *s = NULL;
3662 3663
            int matches = 0;

3664
            if (avformat_open_input(&s, feed->feed_filename, NULL, NULL) >= 0) {
3665 3666 3667 3668 3669 3670 3671 3672 3673 3674
                /* Now see if it matches */
                if (s->nb_streams == feed->nb_streams) {
                    matches = 1;
                    for(i=0;i<s->nb_streams;i++) {
                        AVStream *sf, *ss;
                        sf = feed->streams[i];
                        ss = s->streams[i];

                        if (sf->index != ss->index ||
                            sf->id != ss->id) {
3675
                            http_log("Index & Id do not match for stream %d (%s)\n",
3676
                                   i, feed->feed_filename);
3677 3678 3679 3680
                            matches = 0;
                        } else {
                            AVCodecContext *ccf, *ccs;

3681 3682
                            ccf = sf->codec;
                            ccs = ss->codec;
3683 3684
#define CHECK_CODEC(x)  (ccf->x != ccs->x)

3685
                            if (CHECK_CODEC(codec_id) || CHECK_CODEC(codec_type)) {
3686
                                http_log("Codecs do not match for stream %d\n", i);
3687 3688
                                matches = 0;
                            } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3689
                                http_log("Codec bitrates do not match for stream %d\n", i);
3690
                                matches = 0;
3691
                            } else if (ccf->codec_type == AVMEDIA_TYPE_VIDEO) {
3692 3693
                                if (CHECK_CODEC(time_base.den) ||
                                    CHECK_CODEC(time_base.num) ||
3694 3695
                                    CHECK_CODEC(width) ||
                                    CHECK_CODEC(height)) {
3696
                                    http_log("Codec width, height and framerate do not match for stream %d\n", i);
3697 3698
                                    matches = 0;
                                }
3699
                            } else if (ccf->codec_type == AVMEDIA_TYPE_AUDIO) {
3700 3701 3702
                                if (CHECK_CODEC(sample_rate) ||
                                    CHECK_CODEC(channels) ||
                                    CHECK_CODEC(frame_size)) {
3703
                                    http_log("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3704 3705 3706
                                    matches = 0;
                                }
                            } else {
3707
                                http_log("Unknown codec type\n");
3708 3709 3710
                                matches = 0;
                            }
                        }
3711
                        if (!matches)
3712 3713
                            break;
                    }
3714
                } else
3715
                    http_log("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3716 3717
                        feed->feed_filename, s->nb_streams, feed->nb_streams);

3718
                avformat_close_input(&s);
3719
            } else
3720
                http_log("Deleting feed file '%s' as it appears to be corrupt\n",
3721
                        feed->feed_filename);
3722

3723 3724
            if (!matches) {
                if (feed->readonly) {
3725
                    http_log("Unable to delete feed file '%s' as it is marked readonly\n",
3726 3727 3728
                        feed->feed_filename);
                    exit(1);
                }
3729
                unlink(feed->feed_filename);
3730
            }
3731
        }
3732
        if (avio_check(feed->feed_filename, AVIO_FLAG_WRITE) <= 0) {
3733
            AVFormatContext s1 = {0}, *s = &s1;
Fabrice Bellard's avatar
Fabrice Bellard committed
3734

3735
            if (feed->readonly) {
3736
                http_log("Unable to create feed file '%s' as it is marked readonly\n",
3737 3738 3739 3740
                    feed->feed_filename);
                exit(1);
            }

Fabrice Bellard's avatar
Fabrice Bellard committed
3741
            /* only write the header of the ffm file */
3742
            if (avio_open(&s->pb, feed->feed_filename, AVIO_FLAG_WRITE) < 0) {
3743 3744
                http_log("Could not open output feed file '%s'\n",
                         feed->feed_filename);
Fabrice Bellard's avatar
Fabrice Bellard committed
3745 3746
                exit(1);
            }
3747
            s->oformat = feed->fmt;
Fabrice Bellard's avatar
Fabrice Bellard committed
3748
            s->nb_streams = feed->nb_streams;
Mike William's avatar
Mike William committed
3749
            s->streams = feed->streams;
3750
            if (avformat_write_header(s, NULL) < 0) {
3751
                http_log("Container doesn't supports the required parameters\n");
3752 3753
                exit(1);
            }
3754 3755
            /* XXX: need better api */
            av_freep(&s->priv_data);
3756
            avio_close(s->pb);
Fabrice Bellard's avatar
Fabrice Bellard committed
3757 3758 3759 3760
        }
        /* get feed size and write index */
        fd = open(feed->feed_filename, O_RDONLY);
        if (fd < 0) {
3761
            http_log("Could not open output feed file '%s'\n",
Fabrice Bellard's avatar
Fabrice Bellard committed
3762 3763 3764 3765
                    feed->feed_filename);
            exit(1);
        }

3766
        feed->feed_write_index = FFMAX(ffm_read_write_index(fd), FFM_PACKET_SIZE);
Fabrice Bellard's avatar
Fabrice Bellard committed
3767 3768
        feed->feed_size = lseek(fd, 0, SEEK_END);
        /* ensure that we do not wrap before the end of file */
3769
        if (feed->feed_max_size && feed->feed_max_size < feed->feed_size)
Fabrice Bellard's avatar
Fabrice Bellard committed
3770 3771 3772 3773 3774 3775
            feed->feed_max_size = feed->feed_size;

        close(fd);
    }
}

3776 3777 3778
/* compute the bandwidth used by each stream */
static void compute_bandwidth(void)
{
3779 3780
    unsigned bandwidth;
    int i;
3781
    FFStream *stream;
3782

3783 3784 3785 3786
    for(stream = first_stream; stream != NULL; stream = stream->next) {
        bandwidth = 0;
        for(i=0;i<stream->nb_streams;i++) {
            AVStream *st = stream->streams[i];
3787
            switch(st->codec->codec_type) {
3788 3789
            case AVMEDIA_TYPE_AUDIO:
            case AVMEDIA_TYPE_VIDEO:
3790
                bandwidth += st->codec->bit_rate;
3791 3792 3793 3794 3795 3796 3797 3798 3799
                break;
            default:
                break;
            }
        }
        stream->bandwidth = (bandwidth + 999) / 1000;
    }
}

Fabrice Bellard's avatar
Fabrice Bellard committed
3800
/* add a codec and set the default parameters */
3801
static void add_codec(FFStream *stream, AVCodecContext *av)
Fabrice Bellard's avatar
Fabrice Bellard committed
3802 3803 3804 3805 3806
{
    AVStream *st;

    /* compute default parameters */
    switch(av->codec_type) {
3807
    case AVMEDIA_TYPE_AUDIO:
Fabrice Bellard's avatar
Fabrice Bellard committed
3808 3809 3810 3811 3812 3813 3814
        if (av->bit_rate == 0)
            av->bit_rate = 64000;
        if (av->sample_rate == 0)
            av->sample_rate = 22050;
        if (av->channels == 0)
            av->channels = 1;
        break;
3815
    case AVMEDIA_TYPE_VIDEO:
Fabrice Bellard's avatar
Fabrice Bellard committed
3816 3817
        if (av->bit_rate == 0)
            av->bit_rate = 64000;
3818 3819 3820
        if (av->time_base.num == 0){
            av->time_base.den = 5;
            av->time_base.num = 1;
3821
        }
Fabrice Bellard's avatar
Fabrice Bellard committed
3822 3823 3824 3825
        if (av->width == 0 || av->height == 0) {
            av->width = 160;
            av->height = 128;
        }
3826
        /* Bitrate tolerance is less for streaming */
3827
        if (av->bit_rate_tolerance == 0)
3828 3829
            av->bit_rate_tolerance = FFMAX(av->bit_rate / 4,
                      (int64_t)av->bit_rate*av->time_base.num/av->time_base.den);
3830 3831 3832 3833 3834 3835
        if (av->qmin == 0)
            av->qmin = 3;
        if (av->qmax == 0)
            av->qmax = 31;
        if (av->max_qdiff == 0)
            av->max_qdiff = 3;
3836 3837
        av->qcompress = 0.5;
        av->qblur = 0.5;
3838

3839
        if (!av->nsse_weight)
3840 3841 3842
            av->nsse_weight = 8;

        av->frame_skip_cmp = FF_CMP_DCTMAX;
3843
        if (!av->me_method)
Martin Storsjö's avatar
Martin Storsjö committed
3844
            av->me_method = ME_EPZS;
3845 3846
        av->rc_buffer_aggressivity = 1.0;

3847 3848 3849
        if (!av->rc_eq)
            av->rc_eq = "tex^qComp";
        if (!av->i_quant_factor)
3850
            av->i_quant_factor = -0.8;
3851 3852 3853 3854
        if (!av->b_quant_factor)
            av->b_quant_factor = 1.25;
        if (!av->b_quant_offset)
            av->b_quant_offset = 1.25;
3855 3856
        if (!av->rc_max_rate)
            av->rc_max_rate = av->bit_rate * 2;
3857

3858 3859 3860 3861 3862
        if (av->rc_max_rate && !av->rc_buffer_size) {
            av->rc_buffer_size = av->rc_max_rate;
        }


Fabrice Bellard's avatar
Fabrice Bellard committed
3863
        break;
3864
    default:
3865
        abort();
Fabrice Bellard's avatar
Fabrice Bellard committed
3866 3867 3868 3869 3870
    }

    st = av_mallocz(sizeof(AVStream));
    if (!st)
        return;
3871
    st->codec = avcodec_alloc_context3(NULL);
Fabrice Bellard's avatar
Fabrice Bellard committed
3872
    stream->streams[stream->nb_streams++] = st;
3873
    memcpy(st->codec, av, sizeof(AVCodecContext));
Fabrice Bellard's avatar
Fabrice Bellard committed
3874 3875
}

3876
static enum CodecID opt_audio_codec(const char *arg)
3877
{
3878
    AVCodec *p= avcodec_find_encoder_by_name(arg);
3879

3880
    if (p == NULL || p->type != AVMEDIA_TYPE_AUDIO)
3881 3882 3883 3884 3885
        return CODEC_ID_NONE;

    return p->id;
}

3886
static enum CodecID opt_video_codec(const char *arg)
3887
{
3888
    AVCodec *p= avcodec_find_encoder_by_name(arg);
3889

3890
    if (p == NULL || p->type != AVMEDIA_TYPE_VIDEO)
3891 3892 3893 3894 3895
        return CODEC_ID_NONE;

    return p->id;
}

3896 3897
/* simplistic plugin support */

3898
#if HAVE_DLOPEN
3899
static void load_module(const char *filename)
3900 3901 3902 3903 3904 3905 3906 3907 3908
{
    void *dll;
    void (*init_func)(void);
    dll = dlopen(filename, RTLD_NOW);
    if (!dll) {
        fprintf(stderr, "Could not load module '%s' - %s\n",
                filename, dlerror());
        return;
    }
3909

3910 3911
    init_func = dlsym(dll, "ffserver_module_init");
    if (!init_func) {
3912
        fprintf(stderr,
3913 3914 3915 3916 3917 3918 3919
                "%s: init function 'ffserver_module_init()' not found\n",
                filename);
        dlclose(dll);
    }

    init_func();
}
3920
#endif
3921

3922
static int ffserver_opt_default(const char *opt, const char *arg,
3923 3924
                       AVCodecContext *avctx, int type)
{
3925
    int ret = 0;
3926
    const AVOption *o = av_opt_find(avctx, opt, NULL, type, 0);
3927
    if(o)
3928
        ret = av_opt_set(avctx, opt, arg, 0);
3929
    return ret;
3930 3931
}

3932 3933 3934 3935 3936 3937
static int ffserver_opt_preset(const char *arg,
                       AVCodecContext *avctx, int type,
                       enum CodecID *audio_id, enum CodecID *video_id)
{
    FILE *f=NULL;
    char filename[1000], tmp[1000], tmp2[1000], line[1000];
3938 3939
    int ret = 0;
    AVCodec *codec = avcodec_find_encoder(avctx->codec_id);
3940

3941 3942
    if (!(f = get_preset_file(filename, sizeof(filename), arg, 0,
                              codec ? codec->name : NULL))) {
3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958 3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974
        fprintf(stderr, "File for preset '%s' not found\n", arg);
        return 1;
    }

    while(!feof(f)){
        int e= fscanf(f, "%999[^\n]\n", line) - 1;
        if(line[0] == '#' && !e)
            continue;
        e|= sscanf(line, "%999[^=]=%999[^\n]\n", tmp, tmp2) - 2;
        if(e){
            fprintf(stderr, "%s: Invalid syntax: '%s'\n", filename, line);
            ret = 1;
            break;
        }
        if(!strcmp(tmp, "acodec")){
            *audio_id = opt_audio_codec(tmp2);
        }else if(!strcmp(tmp, "vcodec")){
            *video_id = opt_video_codec(tmp2);
        }else if(!strcmp(tmp, "scodec")){
            /* opt_subtitle_codec(tmp2); */
        }else if(ffserver_opt_default(tmp, tmp2, avctx, type) < 0){
            fprintf(stderr, "%s: Invalid option or argument: '%s', parsed as '%s' = '%s'\n", filename, line, tmp, tmp2);
            ret = 1;
            break;
        }
    }

    fclose(f);

    return ret;
}

3975 3976 3977
static AVOutputFormat *ffserver_guess_format(const char *short_name, const char *filename,
                                             const char *mime_type)
{
3978
    AVOutputFormat *fmt = av_guess_format(short_name, filename, mime_type);
3979 3980 3981 3982 3983 3984

    if (fmt) {
        AVOutputFormat *stream_fmt;
        char stream_format_name[64];

        snprintf(stream_format_name, sizeof(stream_format_name), "%s_stream", fmt->name);
3985
        stream_fmt = av_guess_format(stream_format_name, NULL, NULL);
3986 3987 3988 3989 3990 3991 3992 3993

        if (stream_fmt)
            fmt = stream_fmt;
    }

    return fmt;
}

3994 3995 3996 3997 3998 3999 4000 4001 4002 4003 4004
static void report_config_error(const char *filename, int line_num, int *errors, const char *fmt, ...)
{
    va_list vl;
    va_start(vl, fmt);
    fprintf(stderr, "%s:%d: ", filename, line_num);
    vfprintf(stderr, fmt, vl);
    va_end(vl);

    (*errors)++;
}

4005
static int parse_ffconfig(const char *filename)
Fabrice Bellard's avatar
Fabrice Bellard committed
4006 4007 4008 4009 4010 4011 4012
{
    FILE *f;
    char line[1024];
    char cmd[64];
    char arg[1024];
    const char *p;
    int val, errors, line_num;
4013
    FFStream **last_stream, *stream, *redirect;
4014
    FFStream **last_feed, *feed, *s;
Fabrice Bellard's avatar
Fabrice Bellard committed
4015
    AVCodecContext audio_enc, video_enc;
4016
    enum CodecID audio_id, video_id;
Fabrice Bellard's avatar
Fabrice Bellard committed
4017 4018 4019 4020 4021 4022

    f = fopen(filename, "r");
    if (!f) {
        perror(filename);
        return -1;
    }
4023

Fabrice Bellard's avatar
Fabrice Bellard committed
4024 4025 4026 4027 4028 4029 4030 4031
    errors = 0;
    line_num = 0;
    first_stream = NULL;
    last_stream = &first_stream;
    first_feed = NULL;
    last_feed = &first_feed;
    stream = NULL;
    feed = NULL;
4032
    redirect = NULL;
Fabrice Bellard's avatar
Fabrice Bellard committed
4033 4034
    audio_id = CODEC_ID_NONE;
    video_id = CODEC_ID_NONE;
4035 4036

#define ERROR(...) report_config_error(filename, line_num, &errors, __VA_ARGS__)
Fabrice Bellard's avatar
Fabrice Bellard committed
4037 4038 4039 4040 4041
    for(;;) {
        if (fgets(line, sizeof(line), f) == NULL)
            break;
        line_num++;
        p = line;
4042
        while (isspace(*p))
Fabrice Bellard's avatar
Fabrice Bellard committed
4043 4044 4045 4046 4047
            p++;
        if (*p == '\0' || *p == '#')
            continue;

        get_arg(cmd, sizeof(cmd), &p);
4048

4049
        if (!av_strcasecmp(cmd, "Port")) {
Fabrice Bellard's avatar
Fabrice Bellard committed
4050
            get_arg(arg, sizeof(arg), &p);
4051 4052
            val = atoi(arg);
            if (val < 1 || val > 65536) {
4053
                ERROR("Invalid_port: %s\n", arg);
4054 4055
            }
            my_http_addr.sin_port = htons(val);
4056
        } else if (!av_strcasecmp(cmd, "BindAddress")) {
Fabrice Bellard's avatar
Fabrice Bellard committed
4057
            get_arg(arg, sizeof(arg), &p);
4058
            if (resolve_host(&my_http_addr.sin_addr, arg) != 0) {
4059
                ERROR("%s:%d: Invalid host/IP address: %s\n", arg);
4060
            }
4061
        } else if (!av_strcasecmp(cmd, "NoDaemon")) {
4062
            ffserver_daemon = 0;
4063
        } else if (!av_strcasecmp(cmd, "RTSPPort")) {
4064
            get_arg(arg, sizeof(arg), &p);
4065 4066
            val = atoi(arg);
            if (val < 1 || val > 65536) {
4067
                ERROR("%s:%d: Invalid port: %s\n", arg);
4068 4069
            }
            my_rtsp_addr.sin_port = htons(atoi(arg));
4070
        } else if (!av_strcasecmp(cmd, "RTSPBindAddress")) {
4071
            get_arg(arg, sizeof(arg), &p);
4072
            if (resolve_host(&my_rtsp_addr.sin_addr, arg) != 0) {
4073
                ERROR("Invalid host/IP address: %s\n", arg);
Fabrice Bellard's avatar
Fabrice Bellard committed
4074
            }
4075
        } else if (!av_strcasecmp(cmd, "MaxHTTPConnections")) {
4076 4077 4078
            get_arg(arg, sizeof(arg), &p);
            val = atoi(arg);
            if (val < 1 || val > 65536) {
4079
                ERROR("Invalid MaxHTTPConnections: %s\n", arg);
4080 4081
            }
            nb_max_http_connections = val;
4082
        } else if (!av_strcasecmp(cmd, "MaxClients")) {
Fabrice Bellard's avatar
Fabrice Bellard committed
4083 4084
            get_arg(arg, sizeof(arg), &p);
            val = atoi(arg);
4085
            if (val < 1 || val > nb_max_http_connections) {
4086
                ERROR("Invalid MaxClients: %s\n", arg);
Fabrice Bellard's avatar
Fabrice Bellard committed
4087 4088 4089
            } else {
                nb_max_connections = val;
            }
4090
        } else if (!av_strcasecmp(cmd, "MaxBandwidth")) {
4091
            int64_t llval;
4092
            get_arg(arg, sizeof(arg), &p);
4093 4094
            llval = atoll(arg);
            if (llval < 10 || llval > 10000000) {
4095
                ERROR("Invalid MaxBandwidth: %s\n", arg);
4096
            } else
4097
                max_bandwidth = llval;
4098
        } else if (!av_strcasecmp(cmd, "CustomLog")) {
4099 4100
            if (!ffserver_debug)
                get_arg(logfilename, sizeof(logfilename), &p);
4101
        } else if (!av_strcasecmp(cmd, "<Feed")) {
Fabrice Bellard's avatar
Fabrice Bellard committed
4102 4103 4104 4105
            /*********************************************/
            /* Feed related options */
            char *q;
            if (stream || feed) {
4106
                ERROR("Already in a tag\n");
Fabrice Bellard's avatar
Fabrice Bellard committed
4107 4108 4109 4110 4111 4112
            } else {
                feed = av_mallocz(sizeof(FFStream));
                get_arg(feed->filename, sizeof(feed->filename), &p);
                q = strrchr(feed->filename, '>');
                if (*q)
                    *q = '\0';
4113 4114 4115

                for (s = first_feed; s; s = s->next) {
                    if (!strcmp(feed->filename, s->filename)) {
4116
                        ERROR("Feed '%s' already registered\n", s->filename);
4117 4118 4119
                    }
                }

4120
                feed->fmt = av_guess_format("ffm", NULL, NULL);
Fabrice Bellard's avatar
Fabrice Bellard committed
4121 4122 4123 4124 4125 4126
                /* defaut feed file */
                snprintf(feed->feed_filename, sizeof(feed->feed_filename),
                         "/tmp/%s.ffm", feed->filename);
                feed->feed_max_size = 5 * 1024 * 1024;
                feed->is_feed = 1;
                feed->feed = feed; /* self feeding :-) */
4127 4128 4129 4130 4131 4132 4133

                /* add in stream list */
                *last_stream = feed;
                last_stream = &feed->next;
                /* add in feed list */
                *last_feed = feed;
                last_feed = &feed->next_feed;
Fabrice Bellard's avatar
Fabrice Bellard committed
4134
            }
4135
        } else if (!av_strcasecmp(cmd, "Launch")) {
4136 4137 4138
            if (feed) {
                int i;

4139
                feed->child_argv = av_mallocz(64 * sizeof(char *));
4140

4141
                for (i = 0; i < 62; i++) {
Alex Beregszaszi's avatar
Alex Beregszaszi committed
4142 4143
                    get_arg(arg, sizeof(arg), &p);
                    if (!arg[0])
4144 4145
                        break;

Alex Beregszaszi's avatar
Alex Beregszaszi committed
4146
                    feed->child_argv[i] = av_strdup(arg);
4147 4148 4149 4150
                }

                feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));

4151 4152 4153 4154 4155
                snprintf(feed->child_argv[i], 30+strlen(feed->filename),
                    "http://%s:%d/%s",
                        (my_http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" :
                    inet_ntoa(my_http_addr.sin_addr),
                    ntohs(my_http_addr.sin_port), feed->filename);
4156
            }
4157
        } else if (!av_strcasecmp(cmd, "ReadOnlyFile")) {
4158 4159 4160 4161 4162 4163
            if (feed) {
                get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
                feed->readonly = 1;
            } else if (stream) {
                get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
            }
4164
        } else if (!av_strcasecmp(cmd, "File")) {
Fabrice Bellard's avatar
Fabrice Bellard committed
4165 4166
            if (feed) {
                get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
4167
            } else if (stream)
Fabrice Bellard's avatar
Fabrice Bellard committed
4168
                get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4169
        } else if (!av_strcasecmp(cmd, "Truncate")) {
4170 4171 4172 4173
            if (feed) {
                get_arg(arg, sizeof(arg), &p);
                feed->truncate = strtod(arg, NULL);
            }
4174
        } else if (!av_strcasecmp(cmd, "FileMaxSize")) {
Fabrice Bellard's avatar
Fabrice Bellard committed
4175
            if (feed) {
4176
                char *p1;
Fabrice Bellard's avatar
Fabrice Bellard committed
4177 4178 4179 4180
                double fsize;

                get_arg(arg, sizeof(arg), &p);
                p1 = arg;
4181
                fsize = strtod(p1, &p1);
Fabrice Bellard's avatar
Fabrice Bellard committed
4182 4183 4184 4185 4186 4187 4188 4189 4190 4191 4192
                switch(toupper(*p1)) {
                case 'K':
                    fsize *= 1024;
                    break;
                case 'M':
                    fsize *= 1024 * 1024;
                    break;
                case 'G':
                    fsize *= 1024 * 1024 * 1024;
                    break;
                }
4193
                feed->feed_max_size = (int64_t)fsize;
4194
                if (feed->feed_max_size < FFM_PACKET_SIZE*4) {
4195
                    ERROR("Feed max file size is too small, must be at least %d\n", FFM_PACKET_SIZE*4);
4196
                }
Fabrice Bellard's avatar
Fabrice Bellard committed
4197
            }
4198
        } else if (!av_strcasecmp(cmd, "</Feed>")) {
Fabrice Bellard's avatar
Fabrice Bellard committed
4199
            if (!feed) {
4200
                ERROR("No corresponding <Feed> for </Feed>\n");
Fabrice Bellard's avatar
Fabrice Bellard committed
4201 4202
            }
            feed = NULL;
4203
        } else if (!av_strcasecmp(cmd, "<Stream")) {
Fabrice Bellard's avatar
Fabrice Bellard committed
4204 4205 4206 4207
            /*********************************************/
            /* Stream related options */
            char *q;
            if (stream || feed) {
4208
                ERROR("Already in a tag\n");
Fabrice Bellard's avatar
Fabrice Bellard committed
4209
            } else {
4210
                FFStream *s;
Fabrice Bellard's avatar
Fabrice Bellard committed
4211 4212 4213 4214 4215
                stream = av_mallocz(sizeof(FFStream));
                get_arg(stream->filename, sizeof(stream->filename), &p);
                q = strrchr(stream->filename, '>');
                if (*q)
                    *q = '\0';
4216 4217 4218

                for (s = first_stream; s; s = s->next) {
                    if (!strcmp(stream->filename, s->filename)) {
4219
                        ERROR("Stream '%s' already registered\n", s->filename);
4220 4221 4222
                    }
                }

4223
                stream->fmt = ffserver_guess_format(NULL, stream->filename, NULL);
4224 4225
                avcodec_get_context_defaults2(&video_enc, AVMEDIA_TYPE_VIDEO);
                avcodec_get_context_defaults2(&audio_enc, AVMEDIA_TYPE_AUDIO);
4226

Fabrice Bellard's avatar
Fabrice Bellard committed
4227 4228 4229 4230 4231 4232
                audio_id = CODEC_ID_NONE;
                video_id = CODEC_ID_NONE;
                if (stream->fmt) {
                    audio_id = stream->fmt->audio_codec;
                    video_id = stream->fmt->video_codec;
                }
4233 4234 4235

                *last_stream = stream;
                last_stream = &stream->next;
Fabrice Bellard's avatar
Fabrice Bellard committed
4236
            }
4237
        } else if (!av_strcasecmp(cmd, "Feed")) {
Fabrice Bellard's avatar
Fabrice Bellard committed
4238 4239 4240
            get_arg(arg, sizeof(arg), &p);
            if (stream) {
                FFStream *sfeed;
4241

Fabrice Bellard's avatar
Fabrice Bellard committed
4242 4243 4244 4245 4246 4247
                sfeed = first_feed;
                while (sfeed != NULL) {
                    if (!strcmp(sfeed->filename, arg))
                        break;
                    sfeed = sfeed->next_feed;
                }
4248
                if (!sfeed)
4249
                    ERROR("feed '%s' not defined\n", arg);
4250
                else
Fabrice Bellard's avatar
Fabrice Bellard committed
4251 4252
                    stream->feed = sfeed;
            }
4253
        } else if (!av_strcasecmp(cmd, "Format")) {
Fabrice Bellard's avatar
Fabrice Bellard committed
4254
            get_arg(arg, sizeof(arg), &p);
4255
            if (stream) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
4256 4257 4258 4259 4260 4261 4262 4263
                if (!strcmp(arg, "status")) {
                    stream->stream_type = STREAM_TYPE_STATUS;
                    stream->fmt = NULL;
                } else {
                    stream->stream_type = STREAM_TYPE_LIVE;
                    /* jpeg cannot be used here, so use single frame jpeg */
                    if (!strcmp(arg, "jpeg"))
                        strcpy(arg, "mjpeg");
4264
                    stream->fmt = ffserver_guess_format(arg, NULL, NULL);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
4265
                    if (!stream->fmt) {
4266
                        ERROR("Unknown Format: %s\n", arg);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
4267 4268 4269 4270 4271
                    }
                }
                if (stream->fmt) {
                    audio_id = stream->fmt->audio_codec;
                    video_id = stream->fmt->video_codec;
Fabrice Bellard's avatar
Fabrice Bellard committed
4272
                }
4273
            }
4274
        } else if (!av_strcasecmp(cmd, "InputFormat")) {
4275
            get_arg(arg, sizeof(arg), &p);
4276
            if (stream) {
4277 4278
                stream->ifmt = av_find_input_format(arg);
                if (!stream->ifmt) {
4279
                    ERROR("Unknown input format: %s\n", arg);
4280
                }
4281
            }
4282
        } else if (!av_strcasecmp(cmd, "FaviconURL")) {
4283 4284 4285
            if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
                get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
            } else {
4286
                ERROR("FaviconURL only permitted for status streams\n");
4287
            }
4288
        } else if (!av_strcasecmp(cmd, "Author")) {
4289
            if (stream)
4290
                get_arg(stream->author, sizeof(stream->author), &p);
4291
        } else if (!av_strcasecmp(cmd, "Comment")) {
4292
            if (stream)
4293
                get_arg(stream->comment, sizeof(stream->comment), &p);
4294
        } else if (!av_strcasecmp(cmd, "Copyright")) {
4295
            if (stream)
4296
                get_arg(stream->copyright, sizeof(stream->copyright), &p);
4297
        } else if (!av_strcasecmp(cmd, "Title")) {
4298
            if (stream)
4299
                get_arg(stream->title, sizeof(stream->title), &p);
4300
        } else if (!av_strcasecmp(cmd, "Preroll")) {
4301
            get_arg(arg, sizeof(arg), &p);
4302
            if (stream)
4303
                stream->prebuffer = atof(arg) * 1000;
4304
        } else if (!av_strcasecmp(cmd, "StartSendOnKey")) {
4305
            if (stream)
4306
                stream->send_on_key = 1;
4307
        } else if (!av_strcasecmp(cmd, "AudioCodec")) {
4308 4309 4310
            get_arg(arg, sizeof(arg), &p);
            audio_id = opt_audio_codec(arg);
            if (audio_id == CODEC_ID_NONE) {
4311
                ERROR("Unknown AudioCodec: %s\n", arg);
4312
            }
4313
        } else if (!av_strcasecmp(cmd, "VideoCodec")) {
4314 4315 4316
            get_arg(arg, sizeof(arg), &p);
            video_id = opt_video_codec(arg);
            if (video_id == CODEC_ID_NONE) {
4317
                ERROR("Unknown VideoCodec: %s\n", arg);
4318
            }
4319
        } else if (!av_strcasecmp(cmd, "MaxTime")) {
4320
            get_arg(arg, sizeof(arg), &p);
4321
            if (stream)
4322
                stream->max_time = atof(arg) * 1000;
4323
        } else if (!av_strcasecmp(cmd, "AudioBitRate")) {
Fabrice Bellard's avatar
Fabrice Bellard committed
4324
            get_arg(arg, sizeof(arg), &p);
4325
            if (stream)
4326
                audio_enc.bit_rate = lrintf(atof(arg) * 1000);
4327
        } else if (!av_strcasecmp(cmd, "AudioChannels")) {
Fabrice Bellard's avatar
Fabrice Bellard committed
4328
            get_arg(arg, sizeof(arg), &p);
4329
            if (stream)
Fabrice Bellard's avatar
Fabrice Bellard committed
4330
                audio_enc.channels = atoi(arg);
4331
        } else if (!av_strcasecmp(cmd, "AudioSampleRate")) {
Fabrice Bellard's avatar
Fabrice Bellard committed
4332
            get_arg(arg, sizeof(arg), &p);
4333
            if (stream)
Fabrice Bellard's avatar
Fabrice Bellard committed
4334
                audio_enc.sample_rate = atoi(arg);
4335
        } else if (!av_strcasecmp(cmd, "AudioQuality")) {
4336
            get_arg(arg, sizeof(arg), &p);
4337
            if (stream) {
Michael Niedermayer's avatar
Michael Niedermayer committed
4338
//                audio_enc.quality = atof(arg) * 1000;
4339
            }
4340
        } else if (!av_strcasecmp(cmd, "VideoBitRateRange")) {
4341 4342 4343 4344 4345 4346 4347 4348 4349
            if (stream) {
                int minrate, maxrate;

                get_arg(arg, sizeof(arg), &p);

                if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
                    video_enc.rc_min_rate = minrate * 1000;
                    video_enc.rc_max_rate = maxrate * 1000;
                } else {
4350
                    ERROR("Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n", arg);
4351 4352
                }
            }
4353
        } else if (!av_strcasecmp(cmd, "Debug")) {
4354 4355 4356 4357
            if (stream) {
                get_arg(arg, sizeof(arg), &p);
                video_enc.debug = strtol(arg,0,0);
            }
4358
        } else if (!av_strcasecmp(cmd, "Strict")) {
4359 4360 4361 4362
            if (stream) {
                get_arg(arg, sizeof(arg), &p);
                video_enc.strict_std_compliance = atoi(arg);
            }
4363
        } else if (!av_strcasecmp(cmd, "VideoBufferSize")) {
4364 4365
            if (stream) {
                get_arg(arg, sizeof(arg), &p);
4366
                video_enc.rc_buffer_size = atoi(arg) * 8*1024;
4367
            }
4368
        } else if (!av_strcasecmp(cmd, "VideoBitRateTolerance")) {
4369 4370 4371 4372
            if (stream) {
                get_arg(arg, sizeof(arg), &p);
                video_enc.bit_rate_tolerance = atoi(arg) * 1000;
            }
4373
        } else if (!av_strcasecmp(cmd, "VideoBitRate")) {
Fabrice Bellard's avatar
Fabrice Bellard committed
4374 4375 4376 4377
            get_arg(arg, sizeof(arg), &p);
            if (stream) {
                video_enc.bit_rate = atoi(arg) * 1000;
            }
4378
        } else if (!av_strcasecmp(cmd, "VideoSize")) {
Fabrice Bellard's avatar
Fabrice Bellard committed
4379 4380
            get_arg(arg, sizeof(arg), &p);
            if (stream) {
4381
                av_parse_video_size(&video_enc.width, &video_enc.height, arg);
Fabrice Bellard's avatar
Fabrice Bellard committed
4382 4383
                if ((video_enc.width % 16) != 0 ||
                    (video_enc.height % 16) != 0) {
4384
                    ERROR("Image size must be a multiple of 16\n");
Fabrice Bellard's avatar
Fabrice Bellard committed
4385 4386
                }
            }
4387
        } else if (!av_strcasecmp(cmd, "VideoFrameRate")) {
Fabrice Bellard's avatar
Fabrice Bellard committed
4388 4389
            get_arg(arg, sizeof(arg), &p);
            if (stream) {
4390
                AVRational frame_rate;
4391
                if (av_parse_video_rate(&frame_rate, arg) < 0) {
4392
                    ERROR("Incorrect frame rate: %s\n", arg);
4393 4394 4395 4396
                } else {
                    video_enc.time_base.num = frame_rate.den;
                    video_enc.time_base.den = frame_rate.num;
                }
Fabrice Bellard's avatar
Fabrice Bellard committed
4397
            }
4398
        } else if (!av_strcasecmp(cmd, "VideoGopSize")) {
Fabrice Bellard's avatar
Fabrice Bellard committed
4399
            get_arg(arg, sizeof(arg), &p);
4400
            if (stream)
Fabrice Bellard's avatar
Fabrice Bellard committed
4401
                video_enc.gop_size = atoi(arg);
4402
        } else if (!av_strcasecmp(cmd, "VideoIntraOnly")) {
4403
            if (stream)
Fabrice Bellard's avatar
Fabrice Bellard committed
4404
                video_enc.gop_size = 1;
4405
        } else if (!av_strcasecmp(cmd, "VideoHighQuality")) {
4406
            if (stream)
4407
                video_enc.mb_decision = FF_MB_DECISION_BITS;
4408
        } else if (!av_strcasecmp(cmd, "Video4MotionVector")) {
4409
            if (stream) {
4410
                video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove
4411 4412
                video_enc.flags |= CODEC_FLAG_4MV;
            }
4413 4414
        } else if (!av_strcasecmp(cmd, "AVOptionVideo") ||
                   !av_strcasecmp(cmd, "AVOptionAudio")) {
4415 4416 4417 4418 4419
            char arg2[1024];
            AVCodecContext *avctx;
            int type;
            get_arg(arg, sizeof(arg), &p);
            get_arg(arg2, sizeof(arg2), &p);
4420
            if (!av_strcasecmp(cmd, "AVOptionVideo")) {
4421 4422 4423 4424 4425 4426
                avctx = &video_enc;
                type = AV_OPT_FLAG_VIDEO_PARAM;
            } else {
                avctx = &audio_enc;
                type = AV_OPT_FLAG_AUDIO_PARAM;
            }
4427
            if (ffserver_opt_default(arg, arg2, avctx, type|AV_OPT_FLAG_ENCODING_PARAM)) {
4428
                ERROR("AVOption error: %s %s\n", arg, arg2);
4429
            }
4430 4431
        } else if (!av_strcasecmp(cmd, "AVPresetVideo") ||
                   !av_strcasecmp(cmd, "AVPresetAudio")) {
4432 4433 4434
            AVCodecContext *avctx;
            int type;
            get_arg(arg, sizeof(arg), &p);
4435
            if (!av_strcasecmp(cmd, "AVPresetVideo")) {
4436 4437 4438 4439 4440 4441 4442 4443 4444 4445 4446
                avctx = &video_enc;
                video_enc.codec_id = video_id;
                type = AV_OPT_FLAG_VIDEO_PARAM;
            } else {
                avctx = &audio_enc;
                audio_enc.codec_id = audio_id;
                type = AV_OPT_FLAG_AUDIO_PARAM;
            }
            if (ffserver_opt_preset(arg, avctx, type|AV_OPT_FLAG_ENCODING_PARAM, &audio_id, &video_id)) {
                ERROR("AVPreset error: %s\n", arg);
            }
4447
        } else if (!av_strcasecmp(cmd, "VideoTag")) {
4448
            get_arg(arg, sizeof(arg), &p);
4449
            if ((strlen(arg) == 4) && stream)
4450
                video_enc.codec_tag = MKTAG(arg[0], arg[1], arg[2], arg[3]);
4451
        } else if (!av_strcasecmp(cmd, "BitExact")) {
4452
            if (stream)
4453
                video_enc.flags |= CODEC_FLAG_BITEXACT;
4454
        } else if (!av_strcasecmp(cmd, "DctFastint")) {
4455
            if (stream)
4456
                video_enc.dct_algo  = FF_DCT_FASTINT;
4457
        } else if (!av_strcasecmp(cmd, "IdctSimple")) {
4458
            if (stream)
4459
                video_enc.idct_algo = FF_IDCT_SIMPLE;
4460
        } else if (!av_strcasecmp(cmd, "Qscale")) {
4461 4462 4463 4464 4465
            get_arg(arg, sizeof(arg), &p);
            if (stream) {
                video_enc.flags |= CODEC_FLAG_QSCALE;
                video_enc.global_quality = FF_QP2LAMBDA * atoi(arg);
            }
4466
        } else if (!av_strcasecmp(cmd, "VideoQDiff")) {
4467
            get_arg(arg, sizeof(arg), &p);
4468 4469 4470
            if (stream) {
                video_enc.max_qdiff = atoi(arg);
                if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
4471
                    ERROR("VideoQDiff out of range\n");
4472 4473
                }
            }
4474
        } else if (!av_strcasecmp(cmd, "VideoQMax")) {
4475
            get_arg(arg, sizeof(arg), &p);
4476 4477 4478
            if (stream) {
                video_enc.qmax = atoi(arg);
                if (video_enc.qmax < 1 || video_enc.qmax > 31) {
4479
                    ERROR("VideoQMax out of range\n");
4480 4481
                }
            }
4482
        } else if (!av_strcasecmp(cmd, "VideoQMin")) {
4483
            get_arg(arg, sizeof(arg), &p);
4484 4485 4486
            if (stream) {
                video_enc.qmin = atoi(arg);
                if (video_enc.qmin < 1 || video_enc.qmin > 31) {
4487
                    ERROR("VideoQMin out of range\n");
4488 4489
                }
            }
4490
        } else if (!av_strcasecmp(cmd, "LumaElim")) {
4491
            get_arg(arg, sizeof(arg), &p);
4492
            if (stream)
4493
                video_enc.luma_elim_threshold = atoi(arg);
4494
        } else if (!av_strcasecmp(cmd, "ChromaElim")) {
4495
            get_arg(arg, sizeof(arg), &p);
4496
            if (stream)
4497
                video_enc.chroma_elim_threshold = atoi(arg);
4498
        } else if (!av_strcasecmp(cmd, "LumiMask")) {
4499
            get_arg(arg, sizeof(arg), &p);
4500
            if (stream)
4501
                video_enc.lumi_masking = atof(arg);
4502
        } else if (!av_strcasecmp(cmd, "DarkMask")) {
4503
            get_arg(arg, sizeof(arg), &p);
4504
            if (stream)
4505
                video_enc.dark_masking = atof(arg);
4506
        } else if (!av_strcasecmp(cmd, "NoVideo")) {
Fabrice Bellard's avatar
Fabrice Bellard committed
4507
            video_id = CODEC_ID_NONE;
4508
        } else if (!av_strcasecmp(cmd, "NoAudio")) {
Fabrice Bellard's avatar
Fabrice Bellard committed
4509
            audio_id = CODEC_ID_NONE;
4510
        } else if (!av_strcasecmp(cmd, "ACL")) {
4511
            parse_acl_row(stream, feed, NULL, p, filename, line_num);
4512
        } else if (!av_strcasecmp(cmd, "DynamicACL")) {
4513 4514
            if (stream) {
                get_arg(stream->dynamic_acl, sizeof(stream->dynamic_acl), &p);
4515
            }
4516
        } else if (!av_strcasecmp(cmd, "RTSPOption")) {
4517 4518 4519
            get_arg(arg, sizeof(arg), &p);
            if (stream) {
                av_freep(&stream->rtsp_option);
Alex Beregszaszi's avatar
Alex Beregszaszi committed
4520
                stream->rtsp_option = av_strdup(arg);
4521
            }
4522
        } else if (!av_strcasecmp(cmd, "MulticastAddress")) {
4523 4524
            get_arg(arg, sizeof(arg), &p);
            if (stream) {
4525
                if (resolve_host(&stream->multicast_ip, arg) != 0) {
4526
                    ERROR("Invalid host/IP address: %s\n", arg);
4527 4528
                }
                stream->is_multicast = 1;
4529
                stream->loop = 1; /* default is looping */
4530
            }
4531
        } else if (!av_strcasecmp(cmd, "MulticastPort")) {
4532
            get_arg(arg, sizeof(arg), &p);
4533
            if (stream)
4534
                stream->multicast_port = atoi(arg);
4535
        } else if (!av_strcasecmp(cmd, "MulticastTTL")) {
4536
            get_arg(arg, sizeof(arg), &p);
4537
            if (stream)
4538
                stream->multicast_ttl = atoi(arg);
4539
        } else if (!av_strcasecmp(cmd, "NoLoop")) {
4540
            if (stream)
4541
                stream->loop = 0;
4542
        } else if (!av_strcasecmp(cmd, "</Stream>")) {
Fabrice Bellard's avatar
Fabrice Bellard committed
4543
            if (!stream) {
4544
                ERROR("No corresponding <Stream> for </Stream>\n");
4545
            } else {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
4546 4547
                if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
                    if (audio_id != CODEC_ID_NONE) {
4548
                        audio_enc.codec_type = AVMEDIA_TYPE_AUDIO;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
4549 4550 4551 4552
                        audio_enc.codec_id = audio_id;
                        add_codec(stream, &audio_enc);
                    }
                    if (video_id != CODEC_ID_NONE) {
4553
                        video_enc.codec_type = AVMEDIA_TYPE_VIDEO;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
4554 4555 4556
                        video_enc.codec_id = video_id;
                        add_codec(stream, &video_enc);
                    }
Fabrice Bellard's avatar
Fabrice Bellard committed
4557
                }
Baptiste Coudurier's avatar
Baptiste Coudurier committed
4558
                stream = NULL;
4559
            }
4560
        } else if (!av_strcasecmp(cmd, "<Redirect")) {
4561 4562 4563
            /*********************************************/
            char *q;
            if (stream || feed || redirect) {
4564
                ERROR("Already in a tag\n");
4565 4566 4567 4568 4569 4570 4571 4572 4573 4574 4575
            } else {
                redirect = av_mallocz(sizeof(FFStream));
                *last_stream = redirect;
                last_stream = &redirect->next;

                get_arg(redirect->filename, sizeof(redirect->filename), &p);
                q = strrchr(redirect->filename, '>');
                if (*q)
                    *q = '\0';
                redirect->stream_type = STREAM_TYPE_REDIRECT;
            }
4576
        } else if (!av_strcasecmp(cmd, "URL")) {
4577
            if (redirect)
4578
                get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4579
        } else if (!av_strcasecmp(cmd, "</Redirect>")) {
4580
            if (!redirect) {
4581
                ERROR("No corresponding <Redirect> for </Redirect>\n");
4582
            } else {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
4583
                if (!redirect->feed_filename[0]) {
4584
                    ERROR("No URL found for <Redirect>\n");
Baptiste Coudurier's avatar
Baptiste Coudurier committed
4585 4586
                }
                redirect = NULL;
4587
            }
4588
        } else if (!av_strcasecmp(cmd, "LoadModule")) {
4589
            get_arg(arg, sizeof(arg), &p);
4590
#if HAVE_DLOPEN
4591
            load_module(arg);
4592
#else
4593
            ERROR("Module support not compiled into this version: '%s'\n", arg);
4594
#endif
Fabrice Bellard's avatar
Fabrice Bellard committed
4595
        } else {
4596
            ERROR("Incorrect keyword: '%s'\n", cmd);
Fabrice Bellard's avatar
Fabrice Bellard committed
4597 4598
        }
    }
4599
#undef ERROR
Fabrice Bellard's avatar
Fabrice Bellard committed
4600 4601 4602 4603 4604 4605 4606 4607

    fclose(f);
    if (errors)
        return -1;
    else
        return 0;
}

4608 4609 4610 4611 4612 4613 4614 4615 4616 4617 4618 4619 4620 4621 4622
static void handle_child_exit(int sig)
{
    pid_t pid;
    int status;

    while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
        FFStream *feed;

        for (feed = first_feed; feed; feed = feed->next) {
            if (feed->pid == pid) {
                int uptime = time(0) - feed->pid_start;

                feed->pid = 0;
                fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);

4623
                if (uptime < 30)
4624 4625 4626 4627 4628 4629 4630 4631 4632
                    /* Turn off any more restarts */
                    feed->child_argv = 0;
            }
        }
    }

    need_to_start_children = 1;
}

4633
static void opt_debug(void)
4634 4635 4636
{
    ffserver_debug = 1;
    ffserver_daemon = 0;
4637
    logfilename[0] = '-';
4638 4639
}

4640
static int opt_help(const char *opt, const char *arg)
4641
{
4642
    printf("usage: ffserver [options]\n"
4643 4644 4645
           "Hyper fast multi format Audio/Video streaming server\n");
    printf("\n");
    show_help_options(options, "Main options:\n", 0, 0);
4646
    return 0;
4647 4648 4649
}

static const OptionDef options[] = {
4650
#include "cmdutils_common_opts.h"
4651 4652 4653 4654 4655 4656
    { "n", OPT_BOOL, {(void *)&no_launch }, "enable no-launch mode" },
    { "d", 0, {(void*)opt_debug}, "enable debug mode" },
    { "f", HAS_ARG | OPT_STRING, {(void*)&config_filename }, "use configfile instead of /etc/ffserver.conf", "configfile" },
    { NULL },
};

Fabrice Bellard's avatar
Fabrice Bellard committed
4657 4658
int main(int argc, char **argv)
{
4659
    struct sigaction sigact;
Fabrice Bellard's avatar
Fabrice Bellard committed
4660

4661
    parse_loglevel(argc, argv, options);
4662
    av_register_all();
4663
    avformat_network_init();
Fabrice Bellard's avatar
Fabrice Bellard committed
4664

4665
    show_banner(argc, argv, options);
4666

4667
    my_program_name = argv[0];
4668
    my_program_dir = getcwd(0, 0);
4669
    ffserver_daemon = 1;
4670

4671
    parse_options(NULL, argc, argv, options, NULL);
Fabrice Bellard's avatar
Fabrice Bellard committed
4672

4673
    unsetenv("http_proxy");             /* Kill the http_proxy */
4674

4675
    av_lfg_init(&random_state, av_get_random_seed());
4676

4677 4678 4679 4680 4681
    memset(&sigact, 0, sizeof(sigact));
    sigact.sa_handler = handle_child_exit;
    sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
    sigaction(SIGCHLD, &sigact, 0);

Fabrice Bellard's avatar
Fabrice Bellard committed
4682 4683 4684 4685 4686
    if (parse_ffconfig(config_filename) < 0) {
        fprintf(stderr, "Incorrect config file - exiting.\n");
        exit(1);
    }

4687 4688 4689
    /* open log file if needed */
    if (logfilename[0] != '\0') {
        if (!strcmp(logfilename, "-"))
4690
            logfile = stdout;
4691 4692 4693 4694 4695
        else
            logfile = fopen(logfilename, "a");
        av_log_set_callback(http_av_log);
    }

4696 4697
    build_file_streams();

Fabrice Bellard's avatar
Fabrice Bellard committed
4698 4699
    build_feed_streams();

4700 4701
    compute_bandwidth();

4702 4703 4704 4705 4706 4707 4708 4709 4710 4711 4712 4713 4714 4715 4716 4717
    /* put the process in background and detach it from its TTY */
    if (ffserver_daemon) {
        int pid;

        pid = fork();
        if (pid < 0) {
            perror("fork");
            exit(1);
        } else if (pid > 0) {
            /* parent : exit */
            exit(0);
        } else {
            /* child */
            setsid();
            close(0);
            open("/dev/null", O_RDWR);
4718
            if (strcmp(logfilename, "-") != 0) {
4719 4720 4721 4722
                close(1);
                dup(0);
            }
            close(2);
4723 4724 4725 4726
            dup(0);
        }
    }

Fabrice Bellard's avatar
Fabrice Bellard committed
4727 4728 4729
    /* signal init */
    signal(SIGPIPE, SIG_IGN);

4730 4731 4732
    if (ffserver_daemon)
        chdir("/");

4733
    if (http_server() < 0) {
4734
        http_log("Could not start server\n");
Fabrice Bellard's avatar
Fabrice Bellard committed
4735 4736 4737 4738 4739
        exit(1);
    }

    return 0;
}