ffserver.c 156 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 <stdio.h>
33
#include "libavformat/avformat.h"
34
// FIXME those are internal headers, ffserver _really_ shouldn't use them
35
#include "libavformat/ffm.h"
36 37
#include "libavformat/network.h"
#include "libavformat/os_support.h"
38
#include "libavformat/rtpdec.h"
39
#include "libavformat/rtsp.h"
40
#include "libavformat/avio_internal.h"
41 42 43
#include "libavformat/internal.h"
#include "libavformat/url.h"

44
#include "libavutil/avassert.h"
45
#include "libavutil/avstring.h"
46
#include "libavutil/lfg.h"
47
#include "libavutil/dict.h"
48
#include "libavutil/intreadwrite.h"
49
#include "libavutil/mathematics.h"
50
#include "libavutil/random_seed.h"
51
#include "libavutil/parseutils.h"
52
#include "libavutil/opt.h"
53 54
#include "libavutil/time.h"

Fabrice Bellard's avatar
Fabrice Bellard committed
55 56 57 58
#include <stdarg.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
59
#if HAVE_POLL_H
60
#include <poll.h>
61
#endif
Fabrice Bellard's avatar
Fabrice Bellard committed
62 63
#include <errno.h>
#include <time.h>
64
#include <sys/wait.h>
Fabrice Bellard's avatar
Fabrice Bellard committed
65
#include <signal.h>
66
#if HAVE_DLFCN_H
67
#include <dlfcn.h>
68
#endif
69

70
#include "cmdutils.h"
Fabrice Bellard's avatar
Fabrice Bellard committed
71

72
const char program_name[] = "ffserver";
73
const int program_birth_year = 2000;
74

75 76
static const OptionDef options[];

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

    RTSPSTATE_WAIT_REQUEST,
    RTSPSTATE_SEND_REPLY,
89
    RTSPSTATE_SEND_PACKET,
Fabrice Bellard's avatar
Fabrice Bellard committed
90 91
};

Baptiste Coudurier's avatar
Baptiste Coudurier committed
92
static const char *http_state[] = {
93 94 95
    "HTTP_WAIT_REQUEST",
    "HTTP_SEND_HEADER",

Fabrice Bellard's avatar
Fabrice Bellard committed
96 97 98 99 100
    "SEND_DATA_HEADER",
    "SEND_DATA",
    "SEND_DATA_TRAILER",
    "RECEIVE_DATA",
    "WAIT_FEED",
101 102 103 104
    "READY",

    "RTSP_WAIT_REQUEST",
    "RTSP_SEND_REPLY",
105
    "RTSP_SEND_PACKET",
Fabrice Bellard's avatar
Fabrice Bellard committed
106 107
};

108 109
#define MAX_STREAMS 20

110
#define IOBUFFER_INIT_SIZE 8192
Fabrice Bellard's avatar
Fabrice Bellard committed
111 112

/* timeouts are in ms */
113 114 115
#define HTTP_REQUEST_TIMEOUT (15 * 1000)
#define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)

Fabrice Bellard's avatar
Fabrice Bellard committed
116 117
#define SYNC_TIMEOUT (10 * 1000)

118 119 120 121 122
typedef struct RTSPActionServerSetup {
    uint32_t ipaddr;
    char transport_option[512];
} RTSPActionServerSetup;

123
typedef struct {
124
    int64_t count1, count2;
125
    int64_t time1, time2;
126 127
} DataRateData;

Fabrice Bellard's avatar
Fabrice Bellard committed
128 129 130 131 132 133
/* 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 */
134
    int64_t timeout;
135
    uint8_t *buffer_ptr, *buffer_end;
Fabrice Bellard's avatar
Fabrice Bellard committed
136
    int http_error;
137
    int post;
138 139
    int chunked_encoding;
    int chunk_size;               /* 0 if it needs to be read */
Fabrice Bellard's avatar
Fabrice Bellard committed
140
    struct HTTPContext *next;
141
    int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
142
    int64_t data_count;
Fabrice Bellard's avatar
Fabrice Bellard committed
143 144 145 146
    /* feed input */
    int feed_fd;
    /* input format handling */
    AVFormatContext *fmt_in;
147
    int64_t start_time;            /* In milliseconds - this wraps fairly often */
148
    int64_t first_pts;            /* initial pts value */
149 150 151 152 153 154 155
    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
156 157
    /* output format handling */
    struct FFStream *stream;
158 159 160 161
    /* -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;
162
    AVFormatContext fmt_ctx; /* instance of FFStream for one user */
Fabrice Bellard's avatar
Fabrice Bellard committed
163
    int last_packet_sent; /* true if last data packet was sent */
164
    int suppress_log;
165
    DataRateData datarate;
166
    int wmp_client_id;
167 168 169
    char protocol[16];
    char method[16];
    char url[128];
170
    int buffer_size;
171
    uint8_t *buffer;
172 173
    int is_packetized; /* if true, the stream is packetized */
    int packet_stream_index; /* current stream for output in state machine */
174

175
    /* RTSP state specific */
176
    uint8_t *pb_buffer; /* XXX: use that in all the code */
177
    AVIOContext *pb;
178
    int seq; /* RTSP sequence number */
179

180
    /* RTP state specific */
181
    enum RTSPLowerTransport rtp_protocol;
182 183
    char session_id[32]; /* session id */
    AVFormatContext *rtp_ctx[MAX_STREAMS];
184

185 186 187 188 189 190
    /* 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
191 192 193 194 195 196
} HTTPContext;

/* each generated stream is described here */
enum StreamType {
    STREAM_TYPE_LIVE,
    STREAM_TYPE_STATUS,
197
    STREAM_TYPE_REDIRECT,
Fabrice Bellard's avatar
Fabrice Bellard committed
198 199
};

200 201 202 203 204 205 206 207
enum IPAddressAction {
    IP_ALLOW = 1,
    IP_DENY,
};

typedef struct IPAddressACL {
    struct IPAddressACL *next;
    enum IPAddressAction action;
208
    /* These are in host order */
209 210 211 212
    struct in_addr first;
    struct in_addr last;
} IPAddressACL;

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

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

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

268 269
static struct sockaddr_in my_http_addr;
static struct sockaddr_in my_rtsp_addr;
270

271 272 273 274
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
275

276 277 278 279 280
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
281
static int http_parse_request(HTTPContext *c);
282
static int http_send_data(HTTPContext *c);
283
static void compute_status(HTTPContext *c);
Fabrice Bellard's avatar
Fabrice Bellard committed
284 285 286
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);
287 288 289 290

/* RTSP handling */
static int rtsp_parse_request(HTTPContext *c);
static void rtsp_cmd_describe(HTTPContext *c, const char *url);
291
static void rtsp_cmd_options(HTTPContext *c, const char *url);
292 293 294 295
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);
296

297
/* SDP handling */
298
static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
299 300
                                   struct in_addr my_ip);

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

309 310
static const char *my_program_name;

311
static const char *config_filename;
312

313 314
static int ffserver_debug;
static int no_launch;
315
static int need_to_start_children;
316

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

322
static uint64_t max_bandwidth = 1000;
323
static uint64_t current_bandwidth;
324

325
static int64_t cur_time;           // Making this global saves on passing it around everywhere
326

327
static AVLFG random_state;
328

Fabrice Bellard's avatar
Fabrice Bellard committed
329 330
static FILE *logfile = NULL;

331 332 333 334
static int64_t ffm_read_write_index(int fd)
{
    uint8_t buf[8];

335 336
    if (lseek(fd, 8, SEEK_SET) < 0)
        return AVERROR(EIO);
337 338 339 340 341 342 343 344 345 346 347 348
    if (read(fd, buf, 8) != 8)
        return AVERROR(EIO);
    return AV_RB64(buf);
}

static int ffm_write_write_index(int fd, int64_t pos)
{
    uint8_t buf[8];
    int i;

    for(i=0;i<8;i++)
        buf[i] = (pos >> (56 - i * 8)) & 0xff;
349 350
    if (lseek(fd, 8, SEEK_SET) < 0)
        return AVERROR(EIO);
351 352 353 354 355 356 357 358 359 360 361 362 363
    if (write(fd, buf, 8) != 8)
        return AVERROR(EIO);
    return 8;
}

static void ffm_set_write_index(AVFormatContext *s, int64_t pos,
                                int64_t file_size)
{
    FFMContext *ffm = s->priv_data;
    ffm->write_index = pos;
    ffm->file_size = file_size;
}

364 365 366 367 368 369 370 371
/* FIXME: make ffserver work with IPv6 */
/* 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;
372
        struct addrinfo hints = { 0 };
373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398
        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;
}

399
static char *ctime1(char *buf2, int buf_size)
Baptiste Coudurier's avatar
Baptiste Coudurier committed
400 401 402 403 404 405
{
    time_t ti;
    char *p;

    ti = time(NULL);
    p = ctime(&ti);
406
    av_strlcpy(buf2, p, buf_size);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
407 408 409 410 411 412
    p = buf2 + strlen(p) - 1;
    if (*p == '\n')
        *p = '\0';
    return buf2;
}

413
static void http_vlog(const char *fmt, va_list vargs)
Fabrice Bellard's avatar
Fabrice Bellard committed
414
{
415
    static int print_prefix = 1;
416
    if (logfile) {
417
        if (print_prefix) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
418
            char buf[32];
419
            ctime1(buf, sizeof(buf));
Baptiste Coudurier's avatar
Baptiste Coudurier committed
420
            fprintf(logfile, "%s ", buf);
421 422
        }
        print_prefix = strstr(fmt, "\n") != NULL;
423
        vfprintf(logfile, fmt, vargs);
424 425
        fflush(logfile);
    }
426 427
}

428 429 430 431
#ifdef __GNUC__
__attribute__ ((format (printf, 1, 2)))
#endif
static void http_log(const char *fmt, ...)
432 433 434 435 436 437 438 439 440 441 442
{
    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;
443
    if (level > av_log_get_level())
444 445
        return;
    if (print_prefix && avc)
446
        http_log("[%s @ %p]", avc->item_name(ptr), ptr);
447 448
    print_prefix = strstr(fmt, "\n") != NULL;
    http_vlog(fmt, vargs);
Fabrice Bellard's avatar
Fabrice Bellard committed
449 450
}

451 452
static void log_connection(HTTPContext *c)
{
453
    if (c->suppress_log)
454 455
        return;

456 457
    http_log("%s - - [%s] \"%s %s\" %d %"PRId64"\n",
             inet_ntoa(c->from_addr.sin_addr), c->method, c->url,
458
             c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
459 460
}

461
static void update_datarate(DataRateData *drd, int64_t count)
462 463 464 465
{
    if (!drd->time1 && !drd->count1) {
        drd->time1 = drd->time2 = cur_time;
        drd->count1 = drd->count2 = count;
466
    } else if (cur_time - drd->time2 > 5000) {
Alex Beregszaszi's avatar
Alex Beregszaszi committed
467 468 469 470
        drd->time1 = drd->time2;
        drd->count1 = drd->count2;
        drd->time2 = cur_time;
        drd->count2 = count;
471 472 473 474
    }
}

/* In bytes per second */
475
static int compute_datarate(DataRateData *drd, int64_t count)
476 477 478
{
    if (cur_time == drd->time1)
        return 0;
479

480 481 482
    return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
}

483

484 485
static void start_children(FFStream *feed)
{
486 487 488
    if (no_launch)
        return;

489
    for (; feed; feed = feed->next) {
490 491 492
        if (feed->child_argv && !feed->pid) {
            feed->pid_start = time(0);

493 494 495
            feed->pid = fork();

            if (feed->pid < 0) {
496
                http_log("Unable to create children\n");
497 498 499 500 501 502 503 504
                exit(1);
            }
            if (!feed->pid) {
                /* In child */
                char pathname[1024];
                char *slash;
                int i;

505 506 507 508 509 510 511 512 513
                av_strlcpy(pathname, my_program_name, sizeof(pathname));

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

514
                http_log("Launch command line: ");
Stefano Sabatini's avatar
Stefano Sabatini committed
515 516 517 518
                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");
519

520
                for (i = 3; i < 256; i++)
521
                    close(i);
522

523
                if (!ffserver_debug) {
524 525 526 527 528 529
                    if (!freopen("/dev/null", "r", stdin))
                        http_log("failed to redirect STDIN to /dev/null\n;");
                    if (!freopen("/dev/null", "w", stdout))
                        http_log("failed to redirect STDOUT to /dev/null\n;");
                    if (!freopen("/dev/null", "w", stderr))
                        http_log("failed to redirect STDERR to /dev/null\n;");
530
                }
531

532 533
                signal(SIGPIPE, SIG_DFL);

534 535 536 537 538 539
                execvp(pathname, feed->child_argv);

                _exit(1);
            }
        }
    }
540 541
}

542 543
/* open a listening socket */
static int socket_open_listen(struct sockaddr_in *my_addr)
Fabrice Bellard's avatar
Fabrice Bellard committed
544
{
545
    int server_fd, tmp;
Fabrice Bellard's avatar
Fabrice Bellard committed
546 547 548 549 550 551

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

Fabrice Bellard's avatar
Fabrice Bellard committed
553 554 555
    tmp = 1;
    setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));

556
    my_addr->sin_family = AF_INET;
557
    if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
558 559 560
        char bindmsg[32];
        snprintf(bindmsg, sizeof(bindmsg), "bind(port %d)", ntohs(my_addr->sin_port));
        perror (bindmsg);
561
        closesocket(server_fd);
Fabrice Bellard's avatar
Fabrice Bellard committed
562 563
        return -1;
    }
564

Fabrice Bellard's avatar
Fabrice Bellard committed
565 566
    if (listen (server_fd, 5) < 0) {
        perror ("listen");
567
        closesocket(server_fd);
Fabrice Bellard's avatar
Fabrice Bellard committed
568 569
        return -1;
    }
570
    ff_socket_nonblock(server_fd, 1);
571 572 573 574

    return server_fd;
}

575 576 577 578 579 580
/* start all multicast streams */
static void start_multicast(void)
{
    FFStream *stream;
    char session_id[32];
    HTTPContext *rtp_c;
581
    struct sockaddr_in dest_addr = {0};
582 583 584 585 586
    int default_port, stream_index;

    default_port = 6000;
    for(stream = first_stream; stream != NULL; stream = stream->next) {
        if (stream->is_multicast) {
587 588
            unsigned random0 = av_lfg_get(&random_state);
            unsigned random1 = av_lfg_get(&random_state);
589
            /* open the RTP connection */
590
            snprintf(session_id, sizeof(session_id), "%08x%08x",
591
                     random0, random1);
592 593 594 595 596 597 598 599 600 601 602

            /* 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);

603
            rtp_c = rtp_new_connection(&dest_addr, stream, session_id,
604
                                       RTSP_LOWER_TRANSPORT_UDP_MULTICAST);
605
            if (!rtp_c)
606
                continue;
607

608
            if (open_input_stream(rtp_c, "") < 0) {
609 610
                http_log("Could not open input stream for stream '%s'\n",
                         stream->filename);
611 612 613 614
                continue;
            }

            /* open each RTP stream */
615
            for(stream_index = 0; stream_index < stream->nb_streams;
616
                stream_index++) {
617
                dest_addr.sin_port = htons(stream->multicast_port +
618
                                           2 * stream_index);
619
                if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, NULL) < 0) {
620 621
                    http_log("Could not open output stream '%s/streamid=%d'\n",
                             stream->filename, stream_index);
622
                    exit(1);
623 624 625 626 627 628 629 630
                }
            }

            /* change state to send data */
            rtp_c->state = HTTPSTATE_SEND_DATA;
        }
    }
}
631 632 633 634

/* main loop of the http server */
static int http_server(void)
{
635 636
    int server_fd = 0, rtsp_server_fd = 0;
    int ret, delay, delay1;
637
    struct pollfd *poll_table, *poll_entry;
638 639
    HTTPContext *c, *c_next;

640
    if(!(poll_table = av_mallocz((nb_max_http_connections + 2)*sizeof(*poll_table)))) {
641 642 643 644
        http_log("Impossible to allocate a poll table handling %d connections.\n", nb_max_http_connections);
        return -1;
    }

645
    if (my_http_addr.sin_port) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
646 647 648
        server_fd = socket_open_listen(&my_http_addr);
        if (server_fd < 0)
            return -1;
649
    }
Fabrice Bellard's avatar
Fabrice Bellard committed
650

651
    if (my_rtsp_addr.sin_port) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
652 653 654
        rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
        if (rtsp_server_fd < 0)
            return -1;
655 656 657 658 659 660
    }

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

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

664 665
    start_children(first_feed);

666 667
    start_multicast();

Fabrice Bellard's avatar
Fabrice Bellard committed
668 669
    for(;;) {
        poll_entry = poll_table;
670
        if (server_fd) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
671 672 673
            poll_entry->fd = server_fd;
            poll_entry->events = POLLIN;
            poll_entry++;
674 675
        }
        if (rtsp_server_fd) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
676 677 678
            poll_entry->fd = rtsp_server_fd;
            poll_entry->events = POLLIN;
            poll_entry++;
679
        }
680

Fabrice Bellard's avatar
Fabrice Bellard committed
681 682
        /* wait for events on each HTTP handle */
        c = first_http_ctx;
683
        delay = 1000;
Fabrice Bellard's avatar
Fabrice Bellard committed
684 685 686 687
        while (c != NULL) {
            int fd;
            fd = c->fd;
            switch(c->state) {
688 689
            case HTTPSTATE_SEND_HEADER:
            case RTSPSTATE_SEND_REPLY:
690
            case RTSPSTATE_SEND_PACKET:
Fabrice Bellard's avatar
Fabrice Bellard committed
691 692
                c->poll_entry = poll_entry;
                poll_entry->fd = fd;
693
                poll_entry->events = POLLOUT;
Fabrice Bellard's avatar
Fabrice Bellard committed
694 695 696 697 698
                poll_entry++;
                break;
            case HTTPSTATE_SEND_DATA_HEADER:
            case HTTPSTATE_SEND_DATA:
            case HTTPSTATE_SEND_DATA_TRAILER:
699 700 701 702 703 704 705
                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 {
706 707 708 709 710 711
                    /* 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;
712
                }
Fabrice Bellard's avatar
Fabrice Bellard committed
713
                break;
714
            case HTTPSTATE_WAIT_REQUEST:
Fabrice Bellard's avatar
Fabrice Bellard committed
715 716
            case HTTPSTATE_RECEIVE_DATA:
            case HTTPSTATE_WAIT_FEED:
717
            case RTSPSTATE_WAIT_REQUEST:
Fabrice Bellard's avatar
Fabrice Bellard committed
718 719 720
                /* need to catch errors */
                c->poll_entry = poll_entry;
                poll_entry->fd = fd;
721
                poll_entry->events = POLLIN;/* Maybe this will work */
Fabrice Bellard's avatar
Fabrice Bellard committed
722 723 724 725 726 727 728 729 730 731 732 733
                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 {
734
            ret = poll(poll_table, poll_entry - poll_table, delay);
735 736
            if (ret < 0 && ff_neterrno() != AVERROR(EAGAIN) &&
                ff_neterrno() != AVERROR(EINTR))
737
                return -1;
738
        } while (ret < 0);
739

740
        cur_time = av_gettime() / 1000;
Fabrice Bellard's avatar
Fabrice Bellard committed
741

742 743 744 745 746
        if (need_to_start_children) {
            need_to_start_children = 0;
            start_children(first_feed);
        }

Fabrice Bellard's avatar
Fabrice Bellard committed
747
        /* now handle the events */
748 749 750
        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
751
                /* close and free the connection */
752
                log_connection(c);
753
                close_connection(c);
Fabrice Bellard's avatar
Fabrice Bellard committed
754 755 756 757
            }
        }

        poll_entry = poll_table;
758
        if (server_fd) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
759 760 761 762
            /* new HTTP connection request ? */
            if (poll_entry->revents & POLLIN)
                new_connection(server_fd, 0);
            poll_entry++;
763 764
        }
        if (rtsp_server_fd) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
765 766 767
            /* new RTSP connection request ? */
            if (poll_entry->revents & POLLIN)
                new_connection(rtsp_server_fd, 1);
768
        }
Fabrice Bellard's avatar
Fabrice Bellard committed
769 770 771
    }
}

772 773
/* 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
774
{
775 776 777 778 779 780 781 782 783 784 785 786
    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;
    }
}

787 788
static void http_send_too_busy_reply(int fd)
{
789
    char buffer[400];
790
    int len = snprintf(buffer, sizeof(buffer),
791
                       "HTTP/1.0 503 Server too busy\r\n"
792 793 794 795
                       "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"
796
                       "<p>The number of current connections is %u, and this exceeds the limit of %u.</p>\r\n"
797 798
                       "</body></html>\r\n",
                       nb_connections, nb_max_connections);
799
    av_assert0(len < sizeof(buffer));
800 801 802 803
    send(fd, buffer, len, 0);
}


804 805 806
static void new_connection(int server_fd, int is_rtsp)
{
    struct sockaddr_in from_addr;
807 808
    socklen_t len;
    int fd;
809 810 811
    HTTPContext *c = NULL;

    len = sizeof(from_addr);
812
    fd = accept(server_fd, (struct sockaddr *)&from_addr,
813
                &len);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
814 815
    if (fd < 0) {
        http_log("error during accept %s\n", strerror(errno));
816
        return;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
817
    }
818
    ff_socket_nonblock(fd, 1);
819

820 821
    if (nb_connections >= nb_max_connections) {
        http_send_too_busy_reply(fd);
822
        goto fail;
823
    }
824

825 826 827 828
    /* add a new connection */
    c = av_mallocz(sizeof(HTTPContext));
    if (!c)
        goto fail;
829

830 831 832 833 834 835 836
    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;
837 838 839

    c->next = first_http_ctx;
    first_http_ctx = c;
840
    nb_connections++;
841

842 843 844 845 846 847 848 849 850
    start_wait_request(c, is_rtsp);

    return;

 fail:
    if (c) {
        av_free(c->buffer);
        av_free(c);
    }
851
    closesocket(fd);
852 853 854 855 856 857 858 859 860 861 862 863 864 865
}

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;
866
        if (c1 == c)
867
            *cp = c->next;
868
        else
869 870 871
            cp = &c1->next;
    }

872 873 874 875 876 877
    /* 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;
    }

878 879
    /* remove connection associated resources */
    if (c->fd >= 0)
880
        closesocket(c->fd);
881 882 883 884
    if (c->fmt_in) {
        /* close each frame parser */
        for(i=0;i<c->fmt_in->nb_streams;i++) {
            st = c->fmt_in->streams[i];
885
            if (st->codec->codec)
886
                avcodec_close(st->codec);
887
        }
888
        avformat_close_input(&c->fmt_in);
889 890 891 892
    }

    /* free RTP output streams if any */
    nb_streams = 0;
893
    if (c->stream)
894
        nb_streams = c->stream->nb_streams;
895

896 897 898 899
    for(i=0;i<nb_streams;i++) {
        ctx = c->rtp_ctx[i];
        if (ctx) {
            av_write_trailer(ctx);
900
            av_dict_free(&ctx->metadata);
901
            av_free(ctx->streams[0]);
902 903 904
            av_free(ctx);
        }
        h = c->rtp_handles[i];
905
        if (h)
906
            ffurl_close(h);
907
    }
908

909 910
    ctx = &c->fmt_ctx;

911
    if (!c->last_packet_sent && c->state == HTTPSTATE_SEND_DATA_TRAILER) {
912 913
        if (ctx->oformat) {
            /* prepare header */
914
            if (avio_open_dyn_buf(&ctx->pb) >= 0) {
915
                av_write_trailer(ctx);
916
                av_freep(&c->pb_buffer);
917
                avio_close_dyn_buf(ctx->pb, &c->pb_buffer);
918 919 920 921
            }
        }
    }

922
    for(i=0; i<ctx->nb_streams; i++)
Alex Beregszaszi's avatar
Alex Beregszaszi committed
923
        av_free(ctx->streams[i]);
924 925
    av_freep(&ctx->streams);
    av_freep(&ctx->priv_data);
926

927
    if (c->stream && !c->post && c->stream->stream_type == STREAM_TYPE_LIVE)
928
        current_bandwidth -= c->stream->bandwidth;
929 930 931 932 933 934 935

    /* 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);
    }

936
    av_freep(&c->pb_buffer);
937
    av_freep(&c->packet_buffer);
938 939 940 941 942 943 944 945
    av_free(c->buffer);
    av_free(c);
    nb_connections--;
}

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

Fabrice Bellard's avatar
Fabrice Bellard committed
947 948
    switch(c->state) {
    case HTTPSTATE_WAIT_REQUEST:
949
    case RTSPSTATE_WAIT_REQUEST:
Fabrice Bellard's avatar
Fabrice Bellard committed
950 951 952 953 954 955 956 957 958 959
        /* 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 */
960
    read_loop:
961
        len = recv(c->fd, c->buffer_ptr, 1, 0);
Fabrice Bellard's avatar
Fabrice Bellard committed
962
        if (len < 0) {
963 964
            if (ff_neterrno() != AVERROR(EAGAIN) &&
                ff_neterrno() != AVERROR(EINTR))
Fabrice Bellard's avatar
Fabrice Bellard committed
965 966 967 968
                return -1;
        } else if (len == 0) {
            return -1;
        } else {
969
            /* search for end of request. */
970
            uint8_t *ptr;
Fabrice Bellard's avatar
Fabrice Bellard committed
971 972 973 974 975
            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 */
976 977 978 979 980 981
                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
982 983 984 985
                    return -1;
            } else if (ptr >= c->buffer_end) {
                /* request too long: cannot do anything */
                return -1;
986
            } else goto read_loop;
Fabrice Bellard's avatar
Fabrice Bellard committed
987 988 989 990 991 992 993
        }
        break;

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

994
        /* no need to write if no events */
Fabrice Bellard's avatar
Fabrice Bellard committed
995 996
        if (!(c->poll_entry->revents & POLLOUT))
            return 0;
997
        len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
Fabrice Bellard's avatar
Fabrice Bellard committed
998
        if (len < 0) {
999 1000
            if (ff_neterrno() != AVERROR(EAGAIN) &&
                ff_neterrno() != AVERROR(EINTR)) {
Fabrice Bellard's avatar
Fabrice Bellard committed
1001
                /* error : close connection */
1002
                av_freep(&c->pb_buffer);
Fabrice Bellard's avatar
Fabrice Bellard committed
1003 1004 1005 1006
                return -1;
            }
        } else {
            c->buffer_ptr += len;
1007 1008
            if (c->stream)
                c->stream->bytes_served += len;
1009
            c->data_count += len;
Fabrice Bellard's avatar
Fabrice Bellard committed
1010
            if (c->buffer_ptr >= c->buffer_end) {
1011
                av_freep(&c->pb_buffer);
Fabrice Bellard's avatar
Fabrice Bellard committed
1012
                /* if error, exit */
1013
                if (c->http_error)
Fabrice Bellard's avatar
Fabrice Bellard committed
1014
                    return -1;
1015
                /* all the buffer was sent : synchronize to the incoming stream */
Fabrice Bellard's avatar
Fabrice Bellard committed
1016 1017 1018 1019 1020 1021 1022 1023 1024
                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:
1025 1026 1027 1028 1029 1030
        /* 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;
1031

1032 1033 1034 1035
            /* no need to read if no events */
            if (!(c->poll_entry->revents & POLLOUT))
                return 0;
        }
1036
        if (http_send_data(c) < 0)
Fabrice Bellard's avatar
Fabrice Bellard committed
1037
            return -1;
1038 1039 1040
        /* close connection if trailer sent */
        if (c->state == HTTPSTATE_SEND_DATA_TRAILER)
            return -1;
Fabrice Bellard's avatar
Fabrice Bellard committed
1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052
        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 */
1053
        if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
Fabrice Bellard's avatar
Fabrice Bellard committed
1054 1055 1056 1057
            return -1;

        /* nothing to do, we'll be waken up by incoming feed packets */
        break;
1058 1059 1060 1061 1062 1063 1064 1065 1066

    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;
1067
        len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
1068
        if (len < 0) {
1069 1070
            if (ff_neterrno() != AVERROR(EAGAIN) &&
                ff_neterrno() != AVERROR(EINTR)) {
1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084
                /* 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;
1085 1086 1087 1088 1089 1090 1091 1092
    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;
1093 1094
        len = send(c->fd, c->packet_buffer_ptr,
                    c->packet_buffer_end - c->packet_buffer_ptr, 0);
1095
        if (len < 0) {
1096 1097
            if (ff_neterrno() != AVERROR(EAGAIN) &&
                ff_neterrno() != AVERROR(EINTR)) {
1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110
                /* 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;
1111 1112 1113
    case HTTPSTATE_READY:
        /* nothing to do */
        break;
Fabrice Bellard's avatar
Fabrice Bellard committed
1114 1115 1116 1117 1118 1119
    default:
        return -1;
    }
    return 0;
}

1120 1121 1122 1123 1124
static int extract_rates(char *rates, int ratelen, const char *request)
{
    const char *p;

    for (p = request; *p && *p != '\r' && *p != '\n'; ) {
1125
        if (av_strncasecmp(p, "Pragma:", 7) == 0) {
1126 1127
            const char *q = p + 7;

1128
            while (*q && *q != '\n' && av_isspace(*q))
1129 1130
                q++;

1131
            if (av_strncasecmp(q, "stream-switch-entry=", 20) == 0) {
1132 1133 1134 1135 1136
                int stream_no;
                int rate_no;

                q += 20;

1137
                memset(rates, 0xff, ratelen);
1138 1139 1140 1141 1142

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

1143
                    if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2)
1144
                        break;
1145

1146
                    stream_no--;
1147
                    if (stream_no < ratelen && stream_no >= 0)
1148 1149
                        rates[stream_no] = rate_no;

1150
                    while (*q && *q != '\n' && !av_isspace(*q))
1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166
                        q++;
                }

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

        p++;
    }

    return 0;
}

1167
static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
1168 1169
{
    int i;
1170 1171 1172 1173
    int best_bitrate = 100000000;
    int best = -1;

    for (i = 0; i < feed->nb_streams; i++) {
1174
        AVCodecContext *feed_codec = feed->streams[i]->codec;
1175 1176 1177 1178

        if (feed_codec->codec_id != codec->codec_id ||
            feed_codec->sample_rate != codec->sample_rate ||
            feed_codec->width != codec->width ||
1179
            feed_codec->height != codec->height)
1180 1181 1182 1183
            continue;

        /* Potential stream */

1184
        /* We want the fastest stream less than bit_rate, or the slowest
1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208
         * 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;
1209

1210 1211 1212 1213
    /* Not much we can do for a feed */
    if (!req->feed)
        return 0;

1214
    for (i = 0; i < req->nb_streams; i++) {
1215
        AVCodecContext *codec = req->streams[i]->codec;
1216 1217 1218

        switch(rates[i]) {
            case 0:
1219
                c->switch_feed_streams[i] = req->feed_streams[i];
1220 1221
                break;
            case 1:
1222
                c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
1223 1224
                break;
            case 2:
1225 1226 1227 1228 1229 1230 1231
                /* 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
1232 1233 1234
                break;
        }

1235 1236 1237
        if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
            action_required = 1;
    }
1238

1239 1240
    return action_required;
}
1241

1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260
/* 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;
1261
    while (!av_isspace(*p) && *p != '\0') {
1262 1263 1264 1265 1266 1267 1268 1269 1270
        if ((q - buf) < buf_size - 1)
            *q++ = *p;
        p++;
    }
    if (buf_size > 0)
        *q = '\0';
    *pp = p;
}

1271 1272 1273 1274 1275 1276 1277
static void get_arg(char *buf, int buf_size, const char **pp)
{
    const char *p;
    char *q;
    int quote;

    p = *pp;
1278
    while (av_isspace(*p)) p++;
1279 1280 1281 1282 1283 1284 1285 1286 1287
    q = buf;
    quote = 0;
    if (*p == '\"' || *p == '\'')
        quote = *p++;
    for(;;) {
        if (quote) {
            if (*p == quote)
                break;
        } else {
1288
            if (av_isspace(*p))
1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302
                break;
        }
        if (*p == '\0')
            break;
        if ((q - buf) < buf_size - 1)
            *q++ = *p;
        p++;
    }
    *q = '\0';
    if (quote && *p == quote)
        p++;
    *pp = p;
}

1303 1304 1305 1306 1307 1308 1309 1310
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);
1311
    if (av_strcasecmp(arg, "allow") == 0)
1312
        acl.action = IP_ALLOW;
1313
    else if (av_strcasecmp(arg, "deny") == 0)
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 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391
        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;
1392
        while (av_isspace(*p))
1393 1394 1395 1396 1397
            p++;
        if (*p == '\0' || *p == '#')
            continue;
        get_arg(cmd, sizeof(cmd), &p);

1398
        if (!av_strcasecmp(cmd, "ACL"))
1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418
            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)
1419 1420 1421 1422
{
    enum IPAddressAction last_action = IP_DENY;
    IPAddressACL *acl;
    struct in_addr *src = &c->from_addr.sin_addr;
1423
    unsigned long src_addr = src->s_addr;
1424

1425
    for (acl = in_acl; acl; acl = acl->next) {
1426
        if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr)
1427 1428 1429 1430 1431 1432 1433 1434
            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;
}

1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454
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;
}

1455 1456 1457 1458 1459 1460 1461 1462 1463 1464
/* 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 */
1465
    av_strlcpy(file1, filename, sizeof(file1));
1466 1467 1468 1469
    p = strrchr(file1, '.');
    if (p)
        *p = '\0';
    for(stream = first_stream; stream != NULL; stream = stream->next) {
1470
        av_strlcpy(file2, stream->filename, sizeof(file2));
1471 1472 1473 1474
        p = strrchr(file2, '.');
        if (p)
            *p = '\0';
        if (!strcmp(file1, file2)) {
1475
            av_strlcpy(filename, stream->filename, max_size);
1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489
            break;
        }
    }
}

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

Fabrice Bellard's avatar
Fabrice Bellard committed
1490 1491 1492
/* parse http request and prepare header */
static int http_parse_request(HTTPContext *c)
{
1493 1494
    const char *p;
    char *p1;
1495
    enum RedirType redir_type;
Fabrice Bellard's avatar
Fabrice Bellard committed
1496
    char cmd[32];
1497
    char info[1024], filename[1024];
Fabrice Bellard's avatar
Fabrice Bellard committed
1498 1499 1500 1501 1502
    char url[1024], *q;
    char protocol[32];
    char msg[1024];
    const char *mime_type;
    FFStream *stream;
1503
    int i;
1504
    char ratebuf[32];
1505
    const char *useragent = 0;
Fabrice Bellard's avatar
Fabrice Bellard committed
1506 1507

    p = c->buffer;
1508
    get_word(cmd, sizeof(cmd), &p);
1509
    av_strlcpy(c->method, cmd, sizeof(c->method));
1510

Fabrice Bellard's avatar
Fabrice Bellard committed
1511
    if (!strcmp(cmd, "GET"))
1512
        c->post = 0;
Fabrice Bellard's avatar
Fabrice Bellard committed
1513
    else if (!strcmp(cmd, "POST"))
1514
        c->post = 1;
Fabrice Bellard's avatar
Fabrice Bellard committed
1515 1516 1517
    else
        return -1;

1518
    get_word(url, sizeof(url), &p);
1519
    av_strlcpy(c->url, url, sizeof(c->url));
1520

1521
    get_word(protocol, sizeof(protocol), (const char **)&p);
Fabrice Bellard's avatar
Fabrice Bellard committed
1522 1523
    if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
        return -1;
1524

1525
    av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
1526 1527

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

Fabrice Bellard's avatar
Fabrice Bellard committed
1530
    /* find the filename and the optional info string in the request */
1531 1532 1533 1534
    p1 = strchr(url, '?');
    if (p1) {
        av_strlcpy(info, p1, sizeof(info));
        *p1 = '\0';
1535
    } else
Fabrice Bellard's avatar
Fabrice Bellard committed
1536 1537
        info[0] = '\0';

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

1540
    for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1541
        if (av_strncasecmp(p, "User-Agent:", 11) == 0) {
1542
            useragent = p + 11;
1543
            if (*useragent && *useragent != '\n' && av_isspace(*useragent))
1544 1545 1546 1547 1548 1549 1550 1551 1552 1553
                useragent++;
            break;
        }
        p = strchr(p, '\n');
        if (!p)
            break;

        p++;
    }

1554
    redir_type = REDIR_NONE;
1555
    if (av_match_ext(filename, "asx")) {
1556
        redir_type = REDIR_ASX;
1557
        filename[strlen(filename)-1] = 'f';
1558
    } else if (av_match_ext(filename, "asf") &&
1559
        (!useragent || av_strncasecmp(useragent, "NSPlayer", 8) != 0)) {
1560
        /* if this isn't WMP or lookalike, return the redirector file */
1561
        redir_type = REDIR_ASF;
1562
    } else if (av_match_ext(filename, "rpm,ram")) {
1563
        redir_type = REDIR_RAM;
1564
        strcpy(filename + strlen(filename)-2, "m");
1565
    } else if (av_match_ext(filename, "rtsp")) {
1566
        redir_type = REDIR_RTSP;
1567
        compute_real_filename(filename, sizeof(filename) - 1);
1568
    } else if (av_match_ext(filename, "sdp")) {
1569
        redir_type = REDIR_SDP;
1570
        compute_real_filename(filename, sizeof(filename) - 1);
1571
    }
1572

1573 1574
    // "redirect" / request to index.html
    if (!strlen(filename))
1575
        av_strlcpy(filename, "index.html", sizeof(filename) - 1);
1576

Fabrice Bellard's avatar
Fabrice Bellard committed
1577 1578
    stream = first_stream;
    while (stream != NULL) {
1579
        if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
Fabrice Bellard's avatar
Fabrice Bellard committed
1580 1581 1582 1583
            break;
        stream = stream->next;
    }
    if (stream == NULL) {
1584
        snprintf(msg, sizeof(msg), "File '%s' not found", url);
1585
        http_log("File '%s' not found\n", url);
Fabrice Bellard's avatar
Fabrice Bellard committed
1586 1587
        goto send_error;
    }
1588

1589 1590 1591 1592 1593 1594 1595
    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;
1596
        snprintf(q, c->buffer_size,
1597 1598 1599 1600 1601 1602 1603
                      "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);
1604
        q += strlen(q);
1605 1606 1607 1608 1609 1610 1611
        /* prepare output buffer */
        c->buffer_ptr = c->buffer;
        c->buffer_end = q;
        c->state = HTTPSTATE_SEND_HEADER;
        return 0;
    }

1612 1613
    /* If this is WMP, get the rate information */
    if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1614
        if (modify_current_stream(c, ratebuf)) {
1615
            for (i = 0; i < FF_ARRAY_ELEMS(c->feed_streams); i++) {
1616
                if (c->switch_feed_streams[i] >= 0)
Reinhard Tartler's avatar
Reinhard Tartler committed
1617
                    c->switch_feed_streams[i] = -1;
1618 1619
            }
        }
1620 1621
    }

1622 1623 1624
    if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE)
        current_bandwidth += stream->bandwidth;

Diego Biurrun's avatar
Diego Biurrun committed
1625
    /* If already streaming this feed, do not let start another feeder. */
1626 1627
    if (stream->feed_opened) {
        snprintf(msg, sizeof(msg), "This feed is already being received.");
1628
        http_log("Feed '%s' already being received\n", stream->feed_filename);
1629 1630 1631
        goto send_error;
    }

1632
    if (c->post == 0 && max_bandwidth < current_bandwidth) {
1633
        c->http_error = 503;
1634
        q = c->buffer;
1635
        snprintf(q, c->buffer_size,
1636
                      "HTTP/1.0 503 Server too busy\r\n"
1637 1638 1639 1640
                      "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"
1641 1642
                      "<p>The bandwidth being served (including your stream) is %"PRIu64"kbit/sec, "
                      "and this exceeds the limit of %"PRIu64"kbit/sec.</p>\r\n"
1643
                      "</body></html>\r\n", current_bandwidth, max_bandwidth);
1644
        q += strlen(q);
1645 1646 1647 1648 1649 1650
        /* prepare output buffer */
        c->buffer_ptr = c->buffer;
        c->buffer_end = q;
        c->state = HTTPSTATE_SEND_HEADER;
        return 0;
    }
1651

1652
    if (redir_type != REDIR_NONE) {
1653
        const char *hostinfo = 0;
1654

1655
        for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1656
            if (av_strncasecmp(p, "Host:", 5) == 0) {
1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670
                hostinfo = p + 5;
                break;
            }
            p = strchr(p, '\n');
            if (!p)
                break;

            p++;
        }

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

1671
            while (av_isspace(*hostinfo))
1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684
                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;
1685 1686
                    switch(redir_type) {
                    case REDIR_ASX:
1687
                        snprintf(q, c->buffer_size,
1688 1689 1690 1691 1692 1693 1694
                                      "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);
1695
                        q += strlen(q);
1696 1697
                        break;
                    case REDIR_RAM:
1698
                        snprintf(q, c->buffer_size,
1699 1700 1701 1702 1703
                                      "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);
1704
                        q += strlen(q);
1705 1706
                        break;
                    case REDIR_ASF:
1707
                        snprintf(q, c->buffer_size,
1708 1709 1710 1711 1712
                                      "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);
1713
                        q += strlen(q);
1714 1715 1716 1717 1718
                        break;
                    case REDIR_RTSP:
                        {
                            char hostname[256], *p;
                            /* extract only hostname */
1719
                            av_strlcpy(hostname, hostbuf, sizeof(hostname));
1720 1721 1722
                            p = strrchr(hostname, ':');
                            if (p)
                                *p = '\0';
1723
                            snprintf(q, c->buffer_size,
1724 1725 1726 1727 1728
                                          "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);
1729
                            q += strlen(q);
1730 1731 1732 1733
                        }
                        break;
                    case REDIR_SDP:
                        {
1734
                            uint8_t *sdp_data;
1735 1736
                            int sdp_data_size;
                            socklen_t len;
1737 1738
                            struct sockaddr_in my_addr;

1739
                            snprintf(q, c->buffer_size,
1740 1741 1742
                                          "HTTP/1.0 200 OK\r\n"
                                          "Content-type: application/sdp\r\n"
                                          "\r\n");
1743
                            q += strlen(q);
1744 1745 1746

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

1748
                            /* XXX: should use a dynamic buffer */
1749 1750
                            sdp_data_size = prepare_sdp_description(stream,
                                                                    &sdp_data,
1751 1752 1753 1754 1755 1756 1757 1758 1759 1760
                                                                    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:
1761
                        abort();
1762
                        break;
1763
                    }
1764 1765 1766 1767 1768 1769 1770 1771 1772 1773

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

1774
        snprintf(msg, sizeof(msg), "ASX/RAM file not handled");
1775
        goto send_error;
Fabrice Bellard's avatar
Fabrice Bellard committed
1776 1777
    }

1778
    stream->conns_served++;
1779

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

1782
    if (c->post) {
Fabrice Bellard's avatar
Fabrice Bellard committed
1783 1784
        /* if post, it means a feed is being sent */
        if (!stream->is_feed) {
Diego Biurrun's avatar
Diego Biurrun committed
1785 1786
            /* However it might be a status report from WMP! Let us log the
             * data as it might come in handy one day. */
1787
            const char *logline = 0;
1788
            int client_id = 0;
1789

1790
            for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1791
                if (av_strncasecmp(p, "Pragma: log-line=", 17) == 0) {
1792 1793 1794
                    logline = p;
                    break;
                }
1795
                if (av_strncasecmp(p, "Pragma: client-id=", 18) == 0)
1796
                    client_id = strtol(p + 18, 0, 10);
1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811
                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
1812
                    http_log("%.*s\n", (int) (eol - logline), logline);
1813 1814 1815
                    c->suppress_log = 1;
                }
            }
1816

1817
#ifdef DEBUG
1818
            http_log("\nGot request:\n%s\n", c->buffer);
1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829
#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;
                }

1830 1831
                if (wmpc && modify_current_stream(wmpc, ratebuf))
                    wmpc->switch_pending = 1;
1832
            }
1833

1834
            snprintf(msg, sizeof(msg), "POST command not handled");
1835
            c->stream = 0;
Fabrice Bellard's avatar
Fabrice Bellard committed
1836 1837 1838
            goto send_error;
        }
        if (http_start_receive_data(c) < 0) {
1839
            snprintf(msg, sizeof(msg), "could not open feed");
Fabrice Bellard's avatar
Fabrice Bellard committed
1840 1841 1842 1843 1844 1845 1846
            goto send_error;
        }
        c->http_error = 0;
        c->state = HTTPSTATE_RECEIVE_DATA;
        return 0;
    }

1847
#ifdef DEBUG
1848
    if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0)
1849
        http_log("\nGot request:\n%s\n", c->buffer);
1850 1851
#endif

Fabrice Bellard's avatar
Fabrice Bellard committed
1852
    if (c->stream->stream_type == STREAM_TYPE_STATUS)
1853
        goto send_status;
Fabrice Bellard's avatar
Fabrice Bellard committed
1854 1855 1856

    /* open input stream */
    if (open_input_stream(c, info) < 0) {
1857
        snprintf(msg, sizeof(msg), "Input stream corresponding to '%s' not found", url);
Fabrice Bellard's avatar
Fabrice Bellard committed
1858 1859 1860 1861
        goto send_error;
    }

    /* prepare http header */
1862 1863
    c->buffer[0] = 0;
    av_strlcatf(c->buffer, c->buffer_size, "HTTP/1.0 200 OK\r\n");
Fabrice Bellard's avatar
Fabrice Bellard committed
1864 1865
    mime_type = c->stream->fmt->mime_type;
    if (!mime_type)
1866
        mime_type = "application/x-octet-stream";
1867
    av_strlcatf(c->buffer, c->buffer_size, "Pragma: no-cache\r\n");
Fabrice Bellard's avatar
Fabrice Bellard committed
1868 1869

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

1873
        c->wmp_client_id = av_lfg_get(&random_state);
1874

1875
        av_strlcatf(c->buffer, c->buffer_size, "Server: Cougar 4.1.0.3923\r\nCache-Control: no-cache\r\nPragma: client-id=%d\r\nPragma: features=\"broadcast\"\r\n", c->wmp_client_id);
Fabrice Bellard's avatar
Fabrice Bellard committed
1876
    }
1877 1878 1879
    av_strlcatf(c->buffer, c->buffer_size, "Content-Type: %s\r\n", mime_type);
    av_strlcatf(c->buffer, c->buffer_size, "\r\n");
    q = c->buffer + strlen(c->buffer);
1880

Fabrice Bellard's avatar
Fabrice Bellard committed
1881 1882 1883 1884 1885 1886 1887 1888 1889
    /* 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;
1890
    snprintf(q, c->buffer_size,
1891 1892 1893
                  "HTTP/1.0 404 Not Found\r\n"
                  "Content-type: text/html\r\n"
                  "\r\n"
1894 1895 1896 1897
                  "<html>\n"
                  "<head><title>404 Not Found</title></head>\n"
                  "<body>%s</body>\n"
                  "</html>\n", msg);
1898
    q += strlen(q);
Fabrice Bellard's avatar
Fabrice Bellard committed
1899 1900 1901 1902 1903
    /* prepare output buffer */
    c->buffer_ptr = c->buffer;
    c->buffer_end = q;
    c->state = HTTPSTATE_SEND_HEADER;
    return 0;
1904 1905
 send_status:
    compute_status(c);
Fabrice Bellard's avatar
Fabrice Bellard committed
1906 1907 1908 1909 1910 1911
    c->http_error = 200; /* horrible : we use this value to avoid
                            going to the send data state */
    c->state = HTTPSTATE_SEND_HEADER;
    return 0;
}

1912
static void fmt_bytecount(AVIOContext *pb, int64_t count)
1913
{
1914
    static const char suffix[] = " kMGTP";
1915 1916
    const char *s;

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

1919
    avio_printf(pb, "%"PRId64"%c", count, *s);
1920 1921
}

1922
static void compute_status(HTTPContext *c)
Fabrice Bellard's avatar
Fabrice Bellard committed
1923 1924 1925
{
    HTTPContext *c1;
    FFStream *stream;
1926
    char *p;
Fabrice Bellard's avatar
Fabrice Bellard committed
1927
    time_t ti;
1928
    int i, len;
1929
    AVIOContext *pb;
1930

1931
    if (avio_open_dyn_buf(&pb) < 0) {
1932
        /* XXX: return an error ? */
1933
        c->buffer_ptr = c->buffer;
1934 1935
        c->buffer_end = c->buffer;
        return;
1936
    }
Fabrice Bellard's avatar
Fabrice Bellard committed
1937

1938 1939 1940 1941
    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");
1942

1943
    avio_printf(pb, "<html><head><title>%s Status</title>\n", program_name);
1944
    if (c->stream->feed_filename[0])
1945 1946 1947
        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
1948
    /* format status */
1949 1950 1951
    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
1952 1953
    stream = first_stream;
    while (stream != NULL) {
1954 1955 1956
        char sfilename[1024];
        char *eosf;

1957
        if (stream->feed != stream) {
1958
            av_strlcpy(sfilename, stream->filename, sizeof(sfilename) - 10);
1959 1960
            eosf = sfilename + strlen(sfilename);
            if (eosf - sfilename >= 4) {
1961
                if (strcmp(eosf - 4, ".asf") == 0)
1962
                    strcpy(eosf - 4, ".asx");
1963
                else if (strcmp(eosf - 3, ".rm") == 0)
1964
                    strcpy(eosf - 3, ".ram");
1965
                else if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
1966 1967 1968
                    /* generate a sample RTSP director if
                       unicast. Generate an SDP redirector if
                       multicast */
1969 1970 1971
                    eosf = strrchr(sfilename, '.');
                    if (!eosf)
                        eosf = sfilename + strlen(sfilename);
1972 1973 1974 1975
                    if (stream->is_multicast)
                        strcpy(eosf, ".sdp");
                    else
                        strcpy(eosf, ".rtsp");
1976
                }
1977
            }
1978

1979
            avio_printf(pb, "<tr><td><a href=\"/%s\">%s</a> ",
1980
                         sfilename, stream->filename);
1981
            avio_printf(pb, "<td align=right> %d <td align=right> ",
1982
                        stream->conns_served);
1983
            fmt_bytecount(pb, stream->bytes_served);
1984
            switch(stream->stream_type) {
1985
            case STREAM_TYPE_LIVE: {
1986 1987
                    int audio_bit_rate = 0;
                    int video_bit_rate = 0;
Zdenek Kabelac's avatar
Zdenek Kabelac committed
1988 1989 1990 1991
                    const char *audio_codec_name = "";
                    const char *video_codec_name = "";
                    const char *audio_codec_name_extra = "";
                    const char *video_codec_name_extra = "";
1992 1993 1994

                    for(i=0;i<stream->nb_streams;i++) {
                        AVStream *st = stream->streams[i];
1995 1996
                        AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
                        switch(st->codec->codec_type) {
1997
                        case AVMEDIA_TYPE_AUDIO:
1998
                            audio_bit_rate += st->codec->bit_rate;
1999 2000 2001 2002 2003 2004
                            if (codec) {
                                if (*audio_codec_name)
                                    audio_codec_name_extra = "...";
                                audio_codec_name = codec->name;
                            }
                            break;
2005
                        case AVMEDIA_TYPE_VIDEO:
2006
                            video_bit_rate += st->codec->bit_rate;
2007 2008 2009 2010 2011 2012
                            if (codec) {
                                if (*video_codec_name)
                                    video_codec_name_extra = "...";
                                video_codec_name = codec->name;
                            }
                            break;
2013
                        case AVMEDIA_TYPE_DATA:
2014
                            video_bit_rate += st->codec->bit_rate;
2015
                            break;
2016
                        default:
2017
                            abort();
2018
                        }
Fabrice Bellard's avatar
Fabrice Bellard committed
2019
                    }
2020
                    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",
2021
                                 stream->fmt->name,
2022
                                 stream->bandwidth,
2023 2024
                                 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
                                 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
2025
                    if (stream->feed)
2026
                        avio_printf(pb, "<td>%s", stream->feed->filename);
2027
                    else
2028 2029
                        avio_printf(pb, "<td>%s", stream->feed_filename);
                    avio_printf(pb, "\n");
Fabrice Bellard's avatar
Fabrice Bellard committed
2030
                }
2031 2032
                break;
            default:
2033
                avio_printf(pb, "<td align=center> - <td align=right> - <td align=right> - <td><td align=right> - <td>\n");
2034
                break;
Fabrice Bellard's avatar
Fabrice Bellard committed
2035 2036 2037 2038
            }
        }
        stream = stream->next;
    }
2039
    avio_printf(pb, "</table>\n");
2040 2041 2042 2043

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

2048
#if defined(linux)
2049 2050 2051 2052 2053
                {
                    FILE *pid_stat;
                    char ps_cmd[64];

                    /* This is somewhat linux specific I guess */
2054 2055
                    snprintf(ps_cmd, sizeof(ps_cmd),
                             "ps -o \"%%cpu,cputime\" --no-headers %d",
2056
                             stream->pid);
2057

2058 2059 2060 2061
                    pid_stat = popen(ps_cmd, "r");
                    if (pid_stat) {
                        char cpuperc[10];
                        char cpuused[64];
2062

2063
                        if (fscanf(pid_stat, "%9s %63s", cpuperc,
2064
                                   cpuused) == 2) {
2065
                            avio_printf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
2066 2067 2068
                                         cpuperc, cpuused);
                        }
                        fclose(pid_stat);
2069 2070 2071 2072
                    }
                }
#endif

2073
                avio_printf(pb, "<p>");
2074
            }
2075
            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");
2076 2077 2078

            for (i = 0; i < stream->nb_streams; i++) {
                AVStream *st = stream->streams[i];
2079
                AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
2080
                const char *type = "unknown";
2081 2082 2083
                char parameters[64];

                parameters[0] = 0;
2084

2085
                switch(st->codec->codec_type) {
2086
                case AVMEDIA_TYPE_AUDIO:
2087
                    type = "audio";
2088
                    snprintf(parameters, sizeof(parameters), "%d channel(s), %d Hz", st->codec->channels, st->codec->sample_rate);
2089
                    break;
2090
                case AVMEDIA_TYPE_VIDEO:
2091
                    type = "video";
2092 2093
                    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);
2094 2095
                    break;
                default:
2096
                    abort();
2097
                }
2098
                avio_printf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
2099
                        i, type, st->codec->bit_rate/1000, codec ? codec->name : "", parameters);
2100
            }
2101
            avio_printf(pb, "</table>\n");
2102

2103
        }
2104 2105
        stream = stream->next;
    }
2106

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

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

2113
    avio_printf(pb, "Bandwidth in use: %"PRIu64"k / %"PRIu64"k<br>\n",
2114
                 current_bandwidth, max_bandwidth);
2115

2116 2117
    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
2118 2119
    c1 = first_http_ctx;
    i = 0;
2120
    while (c1 != NULL) {
2121 2122 2123 2124
        int bitrate;
        int j;

        bitrate = 0;
2125 2126
        if (c1->stream) {
            for (j = 0; j < c1->stream->nb_streams; j++) {
2127
                if (!c1->stream->feed)
2128
                    bitrate += c1->stream->streams[j]->codec->bit_rate;
2129 2130
                else if (c1->feed_streams[j] >= 0)
                    bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec->bit_rate;
2131 2132 2133
            }
        }

Fabrice Bellard's avatar
Fabrice Bellard committed
2134 2135
        i++;
        p = inet_ntoa(c1->from_addr.sin_addr);
2136
        avio_printf(pb, "<tr><td><b>%d</b><td>%s%s<td>%s<td>%s<td>%s<td align=right>",
2137 2138
                    i,
                    c1->stream ? c1->stream->filename : "",
2139
                    c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
2140
                    p,
2141 2142 2143
                    c1->protocol,
                    http_state[c1->state]);
        fmt_bytecount(pb, bitrate);
2144
        avio_printf(pb, "<td align=right>");
2145
        fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
2146
        avio_printf(pb, "<td align=right>");
2147
        fmt_bytecount(pb, c1->data_count);
2148
        avio_printf(pb, "\n");
Fabrice Bellard's avatar
Fabrice Bellard committed
2149 2150
        c1 = c1->next;
    }
2151
    avio_printf(pb, "</table>\n");
2152

Fabrice Bellard's avatar
Fabrice Bellard committed
2153 2154 2155
    /* date */
    ti = time(NULL);
    p = ctime(&ti);
2156 2157
    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
2158

2159
    len = avio_close_dyn_buf(pb, &c->pb_buffer);
2160 2161
    c->buffer_ptr = c->pb_buffer;
    c->buffer_end = c->pb_buffer + len;
Fabrice Bellard's avatar
Fabrice Bellard committed
2162 2163 2164 2165 2166 2167
}

static int open_input_stream(HTTPContext *c, const char *info)
{
    char buf[128];
    char input_filename[1024];
2168
    AVFormatContext *s = NULL;
2169
    int buf_size, i, ret;
2170
    int64_t stream_pos;
Fabrice Bellard's avatar
Fabrice Bellard committed
2171 2172 2173 2174

    /* find file name */
    if (c->stream->feed) {
        strcpy(input_filename, c->stream->feed->feed_filename);
2175
        buf_size = FFM_PACKET_SIZE;
Fabrice Bellard's avatar
Fabrice Bellard committed
2176
        /* compute position (absolute time) */
2177
        if (av_find_info_tag(buf, sizeof(buf), "date", info)) {
2178 2179
            if ((ret = av_parse_time(&stream_pos, buf, 0)) < 0)
                return ret;
2180
        } else if (av_find_info_tag(buf, sizeof(buf), "buffer", info)) {
2181
            int prebuffer = strtol(buf, 0, 10);
2182
            stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
2183
        } else
2184
            stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
Fabrice Bellard's avatar
Fabrice Bellard committed
2185 2186
    } else {
        strcpy(input_filename, c->stream->feed_filename);
2187
        buf_size = 0;
Fabrice Bellard's avatar
Fabrice Bellard committed
2188
        /* compute position (relative time) */
2189
        if (av_find_info_tag(buf, sizeof(buf), "date", info)) {
2190 2191
            if ((ret = av_parse_time(&stream_pos, buf, 1)) < 0)
                return ret;
2192
        } else
Fabrice Bellard's avatar
Fabrice Bellard committed
2193 2194 2195 2196 2197 2198
            stream_pos = 0;
    }
    if (input_filename[0] == '\0')
        return -1;

    /* open stream */
2199
    if ((ret = avformat_open_input(&s, input_filename, c->stream->ifmt, &c->stream->in_opts)) < 0) {
2200
        http_log("could not open %s: %d\n", input_filename, ret);
Fabrice Bellard's avatar
Fabrice Bellard committed
2201
        return -1;
2202
    }
2203 2204 2205 2206

    /* set buffer size */
    if (buf_size > 0) ffio_set_buf_size(s->pb, buf_size);

2207
    s->flags |= AVFMT_FLAG_GENPTS;
Fabrice Bellard's avatar
Fabrice Bellard committed
2208
    c->fmt_in = s;
2209
    if (strcmp(s->iformat->name, "ffm") && avformat_find_stream_info(c->fmt_in, NULL) < 0) {
2210
        http_log("Could not find stream info '%s'\n", input_filename);
2211
        avformat_close_input(&s);
2212 2213
        return -1;
    }
2214

2215 2216 2217 2218
    /* 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++) {
2219
        if (c->pts_stream_index == 0 &&
2220
            c->stream->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
2221 2222 2223
            c->pts_stream_index = i;
        }
    }
Fabrice Bellard's avatar
Fabrice Bellard committed
2224

2225
    if (c->fmt_in->iformat->read_seek)
2226
        av_seek_frame(c->fmt_in, -1, stream_pos, 0);
2227 2228 2229
    /* 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
2230 2231 2232
    return 0;
}

2233 2234
/* return the server clock (in us) */
static int64_t get_server_clock(HTTPContext *c)
2235
{
2236
    /* compute current pts value from system time */
2237
    return (cur_time - c->start_time) * 1000;
2238 2239
}

2240 2241 2242
/* return the estimated time at which the current packet must be sent
   (in us) */
static int64_t get_packet_send_clock(HTTPContext *c)
2243
{
2244
    int bytes_left, bytes_sent, frame_bytes;
2245

2246
    frame_bytes = c->cur_frame_bytes;
2247
    if (frame_bytes <= 0)
2248
        return c->cur_pts;
2249
    else {
2250 2251 2252
        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;
2253 2254 2255 2256 2257 2258 2259 2260 2261
    }
}


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

2262
    av_freep(&c->pb_buffer);
2263 2264 2265
    switch(c->state) {
    case HTTPSTATE_SEND_DATA_HEADER:
        memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
2266 2267 2268 2269
        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);
2270

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

2273
        for(i=0;i<c->stream->nb_streams;i++) {
2274
            AVStream *src;
2275
            c->fmt_ctx.streams[i] = av_mallocz(sizeof(AVStream));
2276
            /* if file or feed, then just take streams from FFStream struct */
2277
            if (!c->stream->feed ||
2278
                c->stream->feed == c->stream)
2279
                src = c->stream->streams[i];
2280
            else
2281 2282
                src = c->stream->feed->streams[c->stream->feed_streams[i]];

2283 2284 2285
            *(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
2286 2287
                                           AVStream, not in codec */
        }
2288 2289 2290 2291
        /* set output format parameters */
        c->fmt_ctx.oformat = c->stream->fmt;
        c->fmt_ctx.nb_streams = c->stream->nb_streams;

2292 2293 2294
        c->got_key_frame = 0;

        /* prepare header and save header data in a stream */
2295
        if (avio_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2296 2297 2298
            /* XXX: potential leak */
            return -1;
        }
2299
        c->fmt_ctx.pb->seekable = 0;
2300

2301 2302 2303 2304 2305 2306 2307
        /*
         * 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);

2308
        if (avformat_write_header(&c->fmt_ctx, NULL) < 0) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2309
            http_log("Error writing output header\n");
2310
            return -1;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2311
        }
2312
        av_dict_free(&c->fmt_ctx.metadata);
2313

2314
        len = avio_close_dyn_buf(c->fmt_ctx.pb, &c->pb_buffer);
2315 2316 2317 2318
        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
2319 2320 2321 2322
        c->last_packet_sent = 0;
        break;
    case HTTPSTATE_SEND_DATA:
        /* find a new packet */
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335
        /* 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:
2336 2337 2338
            ret = av_read_frame(c->fmt_in, &pkt);
            if (ret < 0) {
                if (c->stream->feed) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2339 2340 2341 2342
                    /* 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 */
2343 2344 2345
                } else if (ret == AVERROR(EAGAIN)) {
                    /* input not ready, come back later */
                    return 0;
2346
                } else {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2347
                    if (c->stream->loop) {
2348
                        avformat_close_input(&c->fmt_in);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2349 2350 2351 2352 2353 2354 2355
                        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;
2356
                    }
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2357 2358
                }
            } else {
2359
                int source_index = pkt.stream_index;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2360 2361 2362 2363 2364 2365 2366 2367 2368 2369
                /* 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;
2370
                        for(i=0;i<c->stream->nb_streams;i++) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2371
                            if (c->switch_feed_streams[i] == pkt.stream_index)
2372
                                if (pkt.flags & AV_PKT_FLAG_KEY)
Reinhard Tartler's avatar
Reinhard Tartler committed
2373
                                    c->switch_feed_streams[i] = -1;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2374 2375
                            if (c->switch_feed_streams[i] >= 0)
                                c->switch_pending = 1;
2376
                        }
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2377 2378
                    }
                    for(i=0;i<c->stream->nb_streams;i++) {
2379
                        if (c->stream->feed_streams[i] == pkt.stream_index) {
2380
                            AVStream *st = c->fmt_in->streams[source_index];
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2381
                            pkt.stream_index = i;
2382
                            if (pkt.flags & AV_PKT_FLAG_KEY &&
2383
                                (st->codec->codec_type == AVMEDIA_TYPE_VIDEO ||
2384
                                 c->stream->nb_streams == 1))
2385 2386
                                c->got_key_frame = 1;
                            if (!c->stream->send_on_key || c->got_key_frame)
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2387 2388 2389 2390 2391
                                goto send_it;
                        }
                    }
                } else {
                    AVCodecContext *codec;
2392 2393 2394
                    AVStream *ist, *ost;
                send_it:
                    ist = c->fmt_in->streams[source_index];
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2395 2396 2397 2398 2399
                    /* 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
2400
                        c->cur_pts = av_rescale_q(pkt.dts, ist->time_base, AV_TIME_BASE_Q);
2401
                        c->cur_pts -= c->first_pts;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2402
                        c->cur_frame_duration = av_rescale_q(pkt.duration, ist->time_base, AV_TIME_BASE_Q);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2403 2404 2405 2406
                        /* find RTP context */
                        c->packet_stream_index = pkt.stream_index;
                        ctx = c->rtp_ctx[c->packet_stream_index];
                        if(!ctx) {
2407
                            av_free_packet(&pkt);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2408
                            break;
2409
                        }
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2410 2411 2412 2413 2414 2415
                        codec = ctx->streams[0]->codec;
                        /* only one stream per RTP connection */
                        pkt.stream_index = 0;
                    } else {
                        ctx = &c->fmt_ctx;
                        /* Fudge here */
2416
                        codec = ctx->streams[pkt.stream_index]->codec;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2417 2418 2419 2420
                    }

                    if (c->is_packetized) {
                        int max_packet_size;
2421
                        if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP)
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2422 2423
                            max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
                        else
2424
                            max_packet_size = c->rtp_handles[c->packet_stream_index]->max_packet_size;
2425
                        ret = ffio_open_dyn_packet_buf(&ctx->pb, max_packet_size);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2426
                    } else {
2427
                        ret = avio_open_dyn_buf(&ctx->pb);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2428 2429 2430 2431 2432
                    }
                    if (ret < 0) {
                        /* XXX: potential leak */
                        return -1;
                    }
2433 2434
                    ost = ctx->streams[pkt.stream_index];

2435
                    ctx->pb->seekable = 0;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2436
                    if (pkt.dts != AV_NOPTS_VALUE)
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2437
                        pkt.dts = av_rescale_q(pkt.dts, ist->time_base, ost->time_base);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2438
                    if (pkt.pts != AV_NOPTS_VALUE)
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2439 2440
                        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);
2441 2442
                    if (av_write_frame(ctx, &pkt) < 0) {
                        http_log("Error writing frame to output\n");
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2443
                        c->state = HTTPSTATE_SEND_DATA_TRAILER;
2444
                    }
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2445

2446
                    len = avio_close_dyn_buf(ctx->pb, &c->pb_buffer);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2447 2448 2449 2450 2451 2452 2453 2454
                    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;
2455
                    }
Fabrice Bellard's avatar
Fabrice Bellard committed
2456
                }
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2457
                av_free_packet(&pkt);
Fabrice Bellard's avatar
Fabrice Bellard committed
2458
            }
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2459
        }
Fabrice Bellard's avatar
Fabrice Bellard committed
2460 2461 2462 2463
        break;
    default:
    case HTTPSTATE_SEND_DATA_TRAILER:
        /* last packet test ? */
2464
        if (c->last_packet_sent || c->is_packetized)
Fabrice Bellard's avatar
Fabrice Bellard committed
2465
            return -1;
2466
        ctx = &c->fmt_ctx;
Fabrice Bellard's avatar
Fabrice Bellard committed
2467
        /* prepare header */
2468
        if (avio_open_dyn_buf(&ctx->pb) < 0) {
2469 2470 2471
            /* XXX: potential leak */
            return -1;
        }
2472
        c->fmt_ctx.pb->seekable = 0;
2473
        av_write_trailer(ctx);
2474
        len = avio_close_dyn_buf(ctx->pb, &c->pb_buffer);
2475 2476 2477
        c->buffer_ptr = c->pb_buffer;
        c->buffer_end = c->pb_buffer + len;

Fabrice Bellard's avatar
Fabrice Bellard committed
2478 2479 2480 2481 2482 2483 2484
        c->last_packet_sent = 1;
        break;
    }
    return 0;
}

/* should convert the format at the same time */
2485 2486
/* send data starting at c->buffer_ptr to the output connection
   (either UDP or TCP connection) */
2487
static int http_send_data(HTTPContext *c)
Fabrice Bellard's avatar
Fabrice Bellard committed
2488
{
2489
    int len, ret;
Fabrice Bellard's avatar
Fabrice Bellard committed
2490

2491 2492 2493 2494 2495
    for(;;) {
        if (c->buffer_ptr >= c->buffer_end) {
            ret = http_prepare_data(c);
            if (ret < 0)
                return -1;
2496
            else if (ret != 0)
2497 2498
                /* state change requested */
                break;
2499
        } else {
2500 2501 2502 2503 2504 2505 2506
            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;
2507 2508
                    return 0;
                }
2509 2510 2511 2512 2513 2514
                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;
2515 2516 2517 2518 2519 2520 2521 2522 2523 2524
                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;

2525
                if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP) {
2526
                    /* RTP packets are sent inside the RTSP TCP connection */
2527
                    AVIOContext *pb;
2528 2529 2530
                    int interleaved_index, size;
                    uint8_t header[4];
                    HTTPContext *rtsp_c;
2531

2532 2533 2534 2535 2536
                    rtsp_c = c->rtsp_c;
                    /* if no RTSP connection left, error */
                    if (!rtsp_c)
                        return -1;
                    /* if already sending something, then wait. */
2537
                    if (rtsp_c->state != RTSPSTATE_WAIT_REQUEST)
2538
                        break;
2539
                    if (avio_open_dyn_buf(&pb) < 0)
2540 2541 2542 2543 2544 2545 2546 2547 2548 2549
                        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;
2550
                    avio_write(pb, header, 4);
2551 2552
                    /* write RTP packet data */
                    c->buffer_ptr += 4;
2553
                    avio_write(pb, c->buffer_ptr, len);
2554
                    size = avio_close_dyn_buf(pb, &c->packet_buffer);
2555 2556 2557
                    /* prepare asynchronous TCP sending */
                    rtsp_c->packet_buffer_ptr = c->packet_buffer;
                    rtsp_c->packet_buffer_end = c->packet_buffer + size;
2558
                    c->buffer_ptr += len;
2559

2560
                    /* send everything we can NOW */
2561 2562
                    len = send(rtsp_c->fd, rtsp_c->packet_buffer_ptr,
                                rtsp_c->packet_buffer_end - rtsp_c->packet_buffer_ptr, 0);
2563
                    if (len > 0)
2564 2565 2566 2567 2568 2569 2570
                        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;
2571
                    } else
2572 2573 2574 2575
                        /* all data has been sent */
                        av_freep(&c->packet_buffer);
                } else {
                    /* send RTP packet directly in UDP */
2576
                    c->buffer_ptr += 4;
2577 2578
                    ffurl_write(c->rtp_handles[c->packet_stream_index],
                                c->buffer_ptr, len);
2579 2580
                    c->buffer_ptr += len;
                    /* here we continue as we can send several packets per 10 ms slot */
2581 2582 2583
                }
            } else {
                /* TCP data output */
2584
                len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2585
                if (len < 0) {
2586 2587
                    if (ff_neterrno() != AVERROR(EAGAIN) &&
                        ff_neterrno() != AVERROR(EINTR))
2588 2589
                        /* error : close connection */
                        return -1;
2590
                    else
2591
                        return 0;
2592
                } else
2593
                    c->buffer_ptr += len;
2594

2595 2596 2597 2598 2599
                c->data_count += len;
                update_datarate(&c->datarate, c->data_count);
                if (c->stream)
                    c->stream->bytes_served += len;
                break;
2600
            }
Fabrice Bellard's avatar
Fabrice Bellard committed
2601
        }
2602
    } /* for(;;) */
Fabrice Bellard's avatar
Fabrice Bellard committed
2603 2604 2605 2606 2607 2608 2609 2610 2611 2612
    return 0;
}

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

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

2613 2614 2615 2616
    /* Don't permit writing to this one */
    if (c->stream->readonly)
        return -1;

Fabrice Bellard's avatar
Fabrice Bellard committed
2617 2618
    /* open feed */
    fd = open(c->stream->feed_filename, O_RDWR);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2619 2620
    if (fd < 0) {
        http_log("Error opening feeder file: %s\n", strerror(errno));
Fabrice Bellard's avatar
Fabrice Bellard committed
2621
        return -1;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2622
    }
Fabrice Bellard's avatar
Fabrice Bellard committed
2623
    c->feed_fd = fd;
2624

2625 2626 2627 2628
    if (c->stream->truncate) {
        /* truncate feed file */
        ffm_write_write_index(c->feed_fd, FFM_PACKET_SIZE);
        http_log("Truncating feed file '%s'\n", c->stream->feed_filename);
2629 2630 2631 2632
        if (ftruncate(c->feed_fd, FFM_PACKET_SIZE) < 0) {
            http_log("Error truncating feed file: %s\n", strerror(errno));
            return -1;
        }
2633
    } else {
2634 2635 2636 2637
        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;
        }
2638 2639
    }

2640
    c->stream->feed_write_index = FFMAX(ffm_read_write_index(fd), FFM_PACKET_SIZE);
Fabrice Bellard's avatar
Fabrice Bellard committed
2641 2642 2643 2644 2645 2646 2647
    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;
2648
    c->chunked_encoding = !!av_stristr(c->buffer, "Transfer-Encoding: chunked");
Fabrice Bellard's avatar
Fabrice Bellard committed
2649 2650
    return 0;
}
2651

Fabrice Bellard's avatar
Fabrice Bellard committed
2652 2653 2654
static int http_receive_data(HTTPContext *c)
{
    HTTPContext *c1;
2655
    int len, loop_run = 0;
Fabrice Bellard's avatar
Fabrice Bellard committed
2656

2657 2658 2659 2660 2661 2662
    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) {
2663 2664
            if (ff_neterrno() != AVERROR(EAGAIN) &&
                ff_neterrno() != AVERROR(EINTR))
2665 2666
                /* error : close connection */
                goto fail;
2667
            return 0;
2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684
        } 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++;
        }
    }
2685

2686 2687 2688
    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);
2689
        if (len < 0) {
2690 2691
            if (ff_neterrno() != AVERROR(EAGAIN) &&
                ff_neterrno() != AVERROR(EINTR))
2692 2693
                /* error : close connection */
                goto fail;
2694
        } else if (len == 0)
2695 2696
            /* end of connection : close it */
            goto fail;
2697
        else {
2698
            c->chunk_size -= len;
2699 2700
            c->buffer_ptr += len;
            c->data_count += len;
2701
            update_datarate(&c->datarate, c->data_count);
2702 2703 2704
        }
    }

2705 2706 2707 2708 2709 2710 2711 2712
    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
2713
    if (c->buffer_ptr >= c->buffer_end) {
2714
        FFStream *feed = c->stream;
Fabrice Bellard's avatar
Fabrice Bellard committed
2715 2716 2717 2718 2719
        /* a packet has been received : write it in the store, except
           if header */
        if (c->data_count > FFM_PACKET_SIZE) {
            /* XXX: use llseek or url_seek */
            lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2720 2721 2722 2723
            if (write(c->feed_fd, c->buffer, FFM_PACKET_SIZE) < 0) {
                http_log("Error writing to feed file: %s\n", strerror(errno));
                goto fail;
            }
2724

Fabrice Bellard's avatar
Fabrice Bellard committed
2725 2726 2727 2728 2729 2730
            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 */
2731
            if (c->stream->feed_max_size && feed->feed_write_index >= c->stream->feed_max_size)
Fabrice Bellard's avatar
Fabrice Bellard committed
2732 2733 2734
                feed->feed_write_index = FFM_PACKET_SIZE;

            /* write index */
2735 2736 2737 2738
            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
2739 2740 2741

            /* wake up any waiting connections */
            for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2742
                if (c1->state == HTTPSTATE_WAIT_FEED &&
2743
                    c1->stream->feed == c->stream->feed)
Fabrice Bellard's avatar
Fabrice Bellard committed
2744 2745
                    c1->state = HTTPSTATE_SEND_DATA;
            }
2746 2747
        } else {
            /* We have a header in our hands that contains useful data */
2748
            AVFormatContext *s = avformat_alloc_context();
2749
            AVIOContext *pb;
2750
            AVInputFormat *fmt_in;
2751 2752
            int i;

2753 2754 2755
            if (!s)
                goto fail;

2756 2757 2758 2759 2760
            /* use feed output format name to find corresponding input format */
            fmt_in = av_find_input_format(feed->fmt->name);
            if (!fmt_in)
                goto fail;

2761 2762
            pb = avio_alloc_context(c->buffer, c->buffer_end - c->buffer,
                                    0, NULL, NULL, NULL, NULL);
2763
            pb->seekable = 0;
2764

2765 2766
            s->pb = pb;
            if (avformat_open_input(&s, c->stream->feed_filename, fmt_in, NULL) < 0) {
2767 2768 2769
                av_free(pb);
                goto fail;
            }
2770 2771

            /* Now we have the actual streams */
2772
            if (s->nb_streams != feed->nb_streams) {
2773
                avformat_close_input(&s);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2774
                av_free(pb);
2775 2776
                http_log("Feed '%s' stream number does not match registered feed\n",
                         c->stream->feed_filename);
2777 2778
                goto fail;
            }
2779

2780 2781 2782
            for (i = 0; i < s->nb_streams; i++) {
                AVStream *fst = feed->streams[i];
                AVStream *st = s->streams[i];
2783
                avcodec_copy_context(fst->codec, st->codec);
2784
            }
2785

2786
            avformat_close_input(&s);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2787
            av_free(pb);
Fabrice Bellard's avatar
Fabrice Bellard committed
2788 2789 2790 2791 2792 2793 2794 2795
        }
        c->buffer_ptr = c->buffer;
    }

    return 0;
 fail:
    c->stream->feed_opened = 0;
    close(c->feed_fd);
2796 2797 2798 2799 2800 2801
    /* 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
2802 2803 2804
    return -1;
}

2805 2806 2807 2808 2809 2810 2811
/********************************************************************/
/* RTSP handling */

static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
{
    const char *str;
    time_t ti;
2812
    struct tm *tm;
2813 2814 2815
    char buf2[32];

    switch(error_number) {
2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848
    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;
2849 2850 2851 2852
    default:
        str = "Unknown Error";
        break;
    }
2853

2854 2855
    avio_printf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
    avio_printf(c->pb, "CSeq: %d\r\n", c->seq);
2856 2857 2858

    /* output GMT time */
    ti = time(NULL);
2859 2860
    tm = gmtime(&ti);
    strftime(buf2, sizeof(buf2), "%a, %d %b %Y %H:%M:%S", tm);
2861
    avio_printf(c->pb, "Date: %s GMT\r\n", buf2);
2862 2863 2864 2865 2866
}

static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
{
    rtsp_reply_header(c, error_number);
2867
    avio_printf(c->pb, "\r\n");
2868 2869 2870 2871 2872 2873 2874 2875 2876 2877
}

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;
2878
    RTSPMessageHeader header1 = { 0 }, *header = &header1;
2879

2880 2881
    c->buffer_ptr[0] = '\0';
    p = c->buffer;
2882

2883 2884 2885 2886
    get_word(cmd, sizeof(cmd), &p);
    get_word(url, sizeof(url), &p);
    get_word(protocol, sizeof(protocol), &p);

2887 2888 2889
    av_strlcpy(c->method, cmd, sizeof(c->method));
    av_strlcpy(c->url, url, sizeof(c->url));
    av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
2890

2891
    if (avio_open_dyn_buf(&c->pb) < 0) {
2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909
        /* 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 */
    /* skip to next line */
    while (*p != '\n' && *p != '\0')
        p++;
    if (*p == '\n')
        p++;
    while (*p != '\0') {
2910
        p1 = memchr(p, '\n', (char *)c->buffer_ptr - p);
2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923
        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';
2924
        ff_rtsp_parse_line(header, line, NULL, NULL);
2925 2926 2927 2928 2929 2930
        p = p1 + 1;
    }

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

2931
    if (!strcmp(cmd, "DESCRIBE"))
2932
        rtsp_cmd_describe(c, url);
2933
    else if (!strcmp(cmd, "OPTIONS"))
2934
        rtsp_cmd_options(c, url);
2935
    else if (!strcmp(cmd, "SETUP"))
2936
        rtsp_cmd_setup(c, url, header);
2937
    else if (!strcmp(cmd, "PLAY"))
2938
        rtsp_cmd_play(c, url, header);
2939
    else if (!strcmp(cmd, "PAUSE"))
2940
        rtsp_cmd_pause(c, url, header);
2941
    else if (!strcmp(cmd, "TEARDOWN"))
2942
        rtsp_cmd_teardown(c, url, header);
2943
    else
2944
        rtsp_reply_error(c, RTSP_STATUS_METHOD);
2945

2946
 the_end:
2947
    len = avio_close_dyn_buf(c->pb, &c->pb_buffer);
2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958
    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;
}

2959
static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
2960
                                   struct in_addr my_ip)
2961
{
2962
    AVFormatContext *avc;
2963
    AVStream *avs = NULL;
2964
    AVOutputFormat *rtp_format = av_guess_format("rtp", NULL, NULL);
2965
    int i;
2966

2967
    avc =  avformat_alloc_context();
2968
    if (avc == NULL || !rtp_format) {
2969
        return -1;
2970
    }
2971
    avc->oformat = rtp_format;
2972 2973
    av_dict_set(&avc->metadata, "title",
               stream->title[0] ? stream->title : "No Title", 0);
2974 2975 2976 2977 2978
    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);
2979 2980
    } else {
        snprintf(avc->filename, 1024, "rtp://0.0.0.0");
2981
    }
2982

2983 2984 2985 2986 2987 2988 2989
    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;

2990
    for(i = 0; i < stream->nb_streams; i++) {
2991 2992
        avc->streams[i] = &avs[i];
        avc->streams[i]->codec = stream->streams[i]->codec;
2993
    }
2994
    *pbuffer = av_mallocz(2048);
2995
    av_sdp_create(&avc, 1, *pbuffer, 2048);
2996 2997 2998

 sdp_done:
    av_free(avc->streams);
2999
    av_dict_free(&avc->metadata);
3000
    av_free(avc);
3001
    av_free(avs);
3002 3003

    return strlen(*pbuffer);
3004 3005
}

3006 3007 3008
static void rtsp_cmd_options(HTTPContext *c, const char *url)
{
//    rtsp_reply_header(c, RTSP_STATUS_OK);
3009 3010 3011 3012
    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");
3013 3014
}

3015 3016 3017 3018 3019
static void rtsp_cmd_describe(HTTPContext *c, const char *url)
{
    FFStream *stream;
    char path1[1024];
    const char *path;
3020
    uint8_t *content;
3021 3022
    int content_length;
    socklen_t len;
3023
    struct sockaddr_in my_addr;
3024

3025
    /* find which url is asked */
3026
    av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
3027 3028 3029 3030 3031
    path = path1;
    if (*path == '/')
        path++;

    for(stream = first_stream; stream != NULL; stream = stream->next) {
3032 3033
        if (!stream->is_feed &&
            stream->fmt && !strcmp(stream->fmt->name, "rtp") &&
3034 3035 3036 3037 3038 3039 3040 3041 3042 3043
            !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 */
3044 3045 3046 3047 3048

    /* 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);
3049 3050 3051 3052 3053
    if (content_length < 0) {
        rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
        return;
    }
    rtsp_reply_header(c, RTSP_STATUS_OK);
3054 3055 3056 3057
    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");
3058
    avio_write(c->pb, content, content_length);
3059
    av_free(content);
3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075
}

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;
}

3076
static RTSPTransportField *find_transport(RTSPMessageHeader *h, enum RTSPLowerTransport lower_transport)
3077 3078 3079 3080 3081 3082
{
    RTSPTransportField *th;
    int i;

    for(i=0;i<h->nb_transports;i++) {
        th = &h->transports[i];
3083
        if (th->lower_transport == lower_transport)
3084 3085 3086 3087 3088
            return th;
    }
    return NULL;
}

3089
static void rtsp_cmd_setup(HTTPContext *c, const char *url,
3090
                           RTSPMessageHeader *h)
3091 3092
{
    FFStream *stream;
3093
    int stream_index, rtp_port, rtcp_port;
3094 3095 3096 3097 3098 3099 3100
    char buf[1024];
    char path1[1024];
    const char *path;
    HTTPContext *rtp_c;
    RTSPTransportField *th;
    struct sockaddr_in dest_addr;
    RTSPActionServerSetup setup;
3101

3102
    /* find which url is asked */
3103
    av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
3104 3105 3106 3107 3108 3109
    path = path1;
    if (*path == '/')
        path++;

    /* now check each stream */
    for(stream = first_stream; stream != NULL; stream = stream->next) {
3110 3111
        if (!stream->is_feed &&
            stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
3112 3113 3114 3115 3116 3117 3118 3119 3120
            /* 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;
            }
3121

3122 3123
            for(stream_index = 0; stream_index < stream->nb_streams;
                stream_index++) {
3124
                snprintf(buf, sizeof(buf), "%s/streamid=%d",
3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136
                         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 */
3137 3138 3139
    if (h->session_id[0] == '\0') {
        unsigned random0 = av_lfg_get(&random_state);
        unsigned random1 = av_lfg_get(&random_state);
3140
        snprintf(h->session_id, sizeof(h->session_id), "%08x%08x",
3141 3142
                 random0, random1);
    }
3143 3144 3145 3146

    /* find rtp session, and create it if none found */
    rtp_c = find_rtp_session(h->session_id);
    if (!rtp_c) {
3147
        /* always prefer UDP */
3148
        th = find_transport(h, RTSP_LOWER_TRANSPORT_UDP);
3149
        if (!th) {
3150
            th = find_transport(h, RTSP_LOWER_TRANSPORT_TCP);
3151 3152 3153 3154 3155 3156 3157
            if (!th) {
                rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
                return;
            }
        }

        rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id,
3158
                                   th->lower_transport);
3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169
        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;
        }
    }
3170

3171 3172 3173 3174 3175 3176
    /* 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;
    }
3177

3178 3179 3180 3181 3182 3183 3184 3185
    /* 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);
3186
    if (!th || (th->lower_transport == RTSP_LOWER_TRANSPORT_UDP &&
3187 3188 3189 3190 3191 3192 3193 3194 3195
                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);
3196

3197
    /* setup stream */
3198
    if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) {
3199 3200 3201 3202 3203 3204 3205
        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 */
3206
    avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3207 3208

    switch(rtp_c->rtp_protocol) {
3209
    case RTSP_LOWER_TRANSPORT_UDP:
3210 3211
        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]);
3212
        avio_printf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
3213
                    "client_port=%d-%d;server_port=%d-%d",
3214 3215
                    th->client_port_min, th->client_port_max,
                    rtp_port, rtcp_port);
3216
        break;
3217
    case RTSP_LOWER_TRANSPORT_TCP:
3218
        avio_printf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
3219 3220 3221 3222 3223
                    stream_index * 2, stream_index * 2 + 1);
        break;
    default:
        break;
    }
3224
    if (setup.transport_option[0] != '\0')
3225 3226
        avio_printf(c->pb, ";%s", setup.transport_option);
    avio_printf(c->pb, "\r\n");
3227

3228

3229
    avio_printf(c->pb, "\r\n");
3230 3231 3232 3233 3234
}


/* find an rtp connection by using the session ID. Check consistency
   with filename */
3235
static HTTPContext *find_rtp_session_with_url(const char *url,
3236 3237 3238 3239 3240
                                              const char *session_id)
{
    HTTPContext *rtp_c;
    char path1[1024];
    const char *path;
3241
    char buf[1024];
3242
    int s, len;
3243 3244 3245 3246 3247 3248

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

    /* find which url is asked */
3249
    av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
3250 3251 3252
    path = path1;
    if (*path == '/')
        path++;
3253 3254 3255 3256 3257 3258 3259 3260 3261
    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;
      }
    }
3262 3263 3264 3265
    len = strlen(path);
    if (len > 0 && path[len - 1] == '/' &&
        !strncmp(path, rtp_c->stream->filename, len - 1))
        return rtp_c;
3266
    return NULL;
3267 3268
}

3269
static void rtsp_cmd_play(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 3282 3283 3284 3285 3286
    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;
3287

3288 3289 3290
    /* now everything is OK, so we can send the connection parameters */
    rtsp_reply_header(c, RTSP_STATUS_OK);
    /* session ID */
3291 3292
    avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
    avio_printf(c->pb, "\r\n");
3293 3294
}

3295
static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPMessageHeader *h)
3296 3297 3298 3299 3300 3301 3302 3303
{
    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;
    }
3304

3305 3306 3307 3308 3309
    if (rtp_c->state != HTTPSTATE_SEND_DATA &&
        rtp_c->state != HTTPSTATE_WAIT_FEED) {
        rtsp_reply_error(c, RTSP_STATUS_STATE);
        return;
    }
3310

3311
    rtp_c->state = HTTPSTATE_READY;
3312
    rtp_c->first_pts = AV_NOPTS_VALUE;
3313 3314 3315
    /* now everything is OK, so we can send the connection parameters */
    rtsp_reply_header(c, RTSP_STATUS_OK);
    /* session ID */
3316 3317
    avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
    avio_printf(c->pb, "\r\n");
3318 3319
}

3320
static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPMessageHeader *h)
3321 3322 3323 3324 3325 3326 3327 3328
{
    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;
    }
3329

3330 3331 3332
    /* now everything is OK, so we can send the connection parameters */
    rtsp_reply_header(c, RTSP_STATUS_OK);
    /* session ID */
3333
    avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3334
    avio_printf(c->pb, "\r\n");
3335 3336 3337

    /* abort the session */
    close_connection(rtp_c);
3338 3339 3340 3341 3342 3343
}


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

3344
static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
3345
                                       FFStream *stream, const char *session_id,
3346
                                       enum RTSPLowerTransport rtp_protocol)
3347 3348
{
    HTTPContext *c = NULL;
3349
    const char *proto_str;
3350

3351 3352 3353 3354
    /* XXX: should output a warning page when coming
       close to the connection limit */
    if (nb_connections >= nb_max_connections)
        goto fail;
3355

3356 3357 3358 3359
    /* add a new connection */
    c = av_mallocz(sizeof(HTTPContext));
    if (!c)
        goto fail;
3360

3361 3362
    c->fd = -1;
    c->poll_entry = NULL;
3363
    c->from_addr = *from_addr;
3364 3365 3366 3367 3368 3369
    c->buffer_size = IOBUFFER_INIT_SIZE;
    c->buffer = av_malloc(c->buffer_size);
    if (!c->buffer)
        goto fail;
    nb_connections++;
    c->stream = stream;
3370
    av_strlcpy(c->session_id, session_id, sizeof(c->session_id));
3371 3372
    c->state = HTTPSTATE_READY;
    c->is_packetized = 1;
3373 3374
    c->rtp_protocol = rtp_protocol;

3375
    /* protocol is shown in statistics */
3376
    switch(c->rtp_protocol) {
3377
    case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
3378 3379
        proto_str = "MCAST";
        break;
3380
    case RTSP_LOWER_TRANSPORT_UDP:
3381 3382
        proto_str = "UDP";
        break;
3383
    case RTSP_LOWER_TRANSPORT_TCP:
3384 3385 3386 3387 3388 3389
        proto_str = "TCP";
        break;
    default:
        proto_str = "???";
        break;
    }
3390 3391
    av_strlcpy(c->protocol, "RTP/", sizeof(c->protocol));
    av_strlcat(c->protocol, proto_str, sizeof(c->protocol));
3392

3393 3394
    current_bandwidth += stream->bandwidth;

3395 3396 3397
    c->next = first_http_ctx;
    first_http_ctx = c;
    return c;
3398

3399 3400 3401 3402 3403 3404 3405 3406 3407
 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
3408
   command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
3409
   used. */
3410
static int rtp_new_av_stream(HTTPContext *c,
3411 3412
                             int stream_index, struct sockaddr_in *dest_addr,
                             HTTPContext *rtsp_c)
3413 3414 3415 3416
{
    AVFormatContext *ctx;
    AVStream *st;
    char *ipaddr;
3417
    URLContext *h = NULL;
3418
    uint8_t *dummy_buf;
3419
    int max_packet_size;
3420

3421
    /* now we can open the relevant output stream */
3422
    ctx = avformat_alloc_context();
3423 3424
    if (!ctx)
        return -1;
3425
    ctx->oformat = av_guess_format("rtp", NULL, NULL);
3426 3427 3428 3429 3430

    st = av_mallocz(sizeof(AVStream));
    if (!st)
        goto fail;
    ctx->nb_streams = 1;
3431
    ctx->streams = av_mallocz(sizeof(AVStream *) * ctx->nb_streams);
Mike William's avatar
Mike William committed
3432
    if (!ctx->streams)
3433
      goto fail;
3434 3435
    ctx->streams[0] = st;

3436
    if (!c->stream->feed ||
3437
        c->stream->feed == c->stream)
3438
        memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3439
    else
3440
        memcpy(st,
3441 3442
               c->stream->feed->streams[c->stream->feed_streams[stream_index]],
               sizeof(AVStream));
3443
    st->priv_data = NULL;
3444

3445 3446 3447 3448
    /* build destination RTP address */
    ipaddr = inet_ntoa(dest_addr->sin_addr);

    switch(c->rtp_protocol) {
3449 3450
    case RTSP_LOWER_TRANSPORT_UDP:
    case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
3451
        /* RTP/UDP case */
3452

3453 3454 3455 3456 3457 3458 3459
        /* 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),
3460
                     "rtp://%s:%d?multicast=1&ttl=%d",
3461 3462 3463 3464 3465
                     ipaddr, ntohs(dest_addr->sin_port), ttl);
        } else {
            snprintf(ctx->filename, sizeof(ctx->filename),
                     "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
        }
3466

3467
        if (ffurl_open(&h, ctx->filename, AVIO_FLAG_WRITE, NULL, NULL) < 0)
3468 3469
            goto fail;
        c->rtp_handles[stream_index] = h;
3470
        max_packet_size = h->max_packet_size;
3471
        break;
3472
    case RTSP_LOWER_TRANSPORT_TCP:
3473 3474 3475 3476 3477
        /* RTP/TCP case */
        c->rtsp_c = rtsp_c;
        max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
        break;
    default:
3478 3479 3480
        goto fail;
    }

3481
    http_log("%s:%d - - \"PLAY %s/streamid=%d %s\"\n",
3482
             ipaddr, ntohs(dest_addr->sin_port),
3483
             c->stream->filename, stream_index, c->protocol);
3484

3485
    /* normally, no packets should be output here, but the packet size may be checked */
3486
    if (ffio_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) {
3487 3488 3489
        /* XXX: close stream */
        goto fail;
    }
3490
    if (avformat_write_header(ctx, NULL) < 0) {
3491 3492
    fail:
        if (h)
3493
            ffurl_close(h);
3494 3495 3496
        av_free(ctx);
        return -1;
    }
3497
    avio_close_dyn_buf(ctx->pb, &dummy_buf);
3498
    av_free(dummy_buf);
3499

3500 3501 3502 3503 3504 3505 3506
    c->rtp_ctx[stream_index] = ctx;
    return 0;
}

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

3507
static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec, int copy)
3508 3509 3510
{
    AVStream *fst;

3511 3512 3513
    if(stream->nb_streams >= FF_ARRAY_ELEMS(stream->streams))
        return NULL;

3514 3515 3516
    fst = av_mallocz(sizeof(AVStream));
    if (!fst)
        return NULL;
3517
    if (copy) {
3518
        fst->codec = avcodec_alloc_context3(NULL);
3519 3520
        memcpy(fst->codec, codec, sizeof(AVCodecContext));
        if (codec->extradata_size) {
3521
            fst->codec->extradata = av_mallocz(codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE);
3522 3523 3524 3525 3526 3527 3528 3529 3530
            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;
    }
3531
    fst->priv_data = av_mallocz(sizeof(FeedData));
3532
    fst->index = stream->nb_streams;
3533
    avpriv_set_pts_info(fst, 33, 1, 90000);
3534
    fst->sample_aspect_ratio = codec->sample_aspect_ratio;
3535 3536 3537 3538
    stream->streams[stream->nb_streams++] = fst;
    return fst;
}

Fabrice Bellard's avatar
Fabrice Bellard committed
3539
/* return the stream number in the feed */
3540
static int add_av_stream(FFStream *feed, AVStream *st)
Fabrice Bellard's avatar
Fabrice Bellard committed
3541 3542 3543 3544 3545
{
    AVStream *fst;
    AVCodecContext *av, *av1;
    int i;

3546
    av = st->codec;
Fabrice Bellard's avatar
Fabrice Bellard committed
3547 3548
    for(i=0;i<feed->nb_streams;i++) {
        st = feed->streams[i];
3549
        av1 = st->codec;
3550 3551
        if (av1->codec_id == av->codec_id &&
            av1->codec_type == av->codec_type &&
Fabrice Bellard's avatar
Fabrice Bellard committed
3552 3553 3554
            av1->bit_rate == av->bit_rate) {

            switch(av->codec_type) {
3555
            case AVMEDIA_TYPE_AUDIO:
Fabrice Bellard's avatar
Fabrice Bellard committed
3556 3557
                if (av1->channels == av->channels &&
                    av1->sample_rate == av->sample_rate)
3558
                    return i;
Fabrice Bellard's avatar
Fabrice Bellard committed
3559
                break;
3560
            case AVMEDIA_TYPE_VIDEO:
Fabrice Bellard's avatar
Fabrice Bellard committed
3561 3562
                if (av1->width == av->width &&
                    av1->height == av->height &&
3563 3564
                    av1->time_base.den == av->time_base.den &&
                    av1->time_base.num == av->time_base.num &&
Fabrice Bellard's avatar
Fabrice Bellard committed
3565
                    av1->gop_size == av->gop_size)
3566
                    return i;
Fabrice Bellard's avatar
Fabrice Bellard committed
3567
                break;
3568
            default:
3569
                abort();
Fabrice Bellard's avatar
Fabrice Bellard committed
3570 3571 3572
            }
        }
    }
3573

3574
    fst = add_av_stream1(feed, av, 0);
Fabrice Bellard's avatar
Fabrice Bellard committed
3575 3576 3577 3578 3579
    if (!fst)
        return -1;
    return feed->nb_streams - 1;
}

3580
static void remove_stream(FFStream *stream)
3581 3582 3583 3584
{
    FFStream **ps;
    ps = &first_stream;
    while (*ps != NULL) {
3585
        if (*ps == stream)
3586
            *ps = (*ps)->next;
3587
        else
3588 3589 3590 3591
            ps = &(*ps)->next;
    }
}

3592
/* specific mpeg4 handling : we extract the raw parameters */
3593
static void extract_mpeg4_header(AVFormatContext *infile)
3594 3595 3596 3597
{
    int mpeg4_count, i, size;
    AVPacket pkt;
    AVStream *st;
3598
    const uint8_t *p;
3599

3600 3601
    infile->flags |= AVFMT_FLAG_NOFILLIN | AVFMT_FLAG_NOPARSE;

3602 3603 3604
    mpeg4_count = 0;
    for(i=0;i<infile->nb_streams;i++) {
        st = infile->streams[i];
3605
        if (st->codec->codec_id == AV_CODEC_ID_MPEG4 &&
3606
            st->codec->extradata_size == 0) {
3607 3608 3609 3610 3611 3612
            mpeg4_count++;
        }
    }
    if (!mpeg4_count)
        return;

3613
    printf("MPEG4 without extra data: trying to find header in %s\n", infile->filename);
3614
    while (mpeg4_count > 0) {
3615
        if (av_read_frame(infile, &pkt) < 0)
3616 3617
            break;
        st = infile->streams[pkt.stream_index];
3618
        if (st->codec->codec_id == AV_CODEC_ID_MPEG4 &&
3619 3620
            st->codec->extradata_size == 0) {
            av_freep(&st->codec->extradata);
3621 3622 3623 3624 3625
            /* 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 */
3626
                if (p[0] == 0x00 && p[1] == 0x00 &&
3627 3628
                    p[2] == 0x01 && p[3] == 0xb6) {
                    size = p - pkt.data;
3629
                    //                    av_hex_dump_log(infile, AV_LOG_DEBUG, pkt.data, size);
3630
                    st->codec->extradata = av_mallocz(size + FF_INPUT_BUFFER_PADDING_SIZE);
3631 3632
                    st->codec->extradata_size = size;
                    memcpy(st->codec->extradata, pkt.data, size);
3633 3634 3635 3636 3637 3638 3639 3640 3641 3642
                    break;
                }
                p++;
            }
            mpeg4_count--;
        }
        av_free_packet(&pkt);
    }
}

3643
/* compute the needed AVStream for each file */
3644
static void build_file_streams(void)
3645 3646
{
    FFStream *stream, *stream_next;
3647
    int i, ret;
3648 3649 3650

    /* gather all streams */
    for(stream = first_stream; stream != NULL; stream = stream_next) {
3651
        AVFormatContext *infile = NULL;
3652 3653 3654 3655 3656 3657
        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 */
3658
            if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
3659 3660
                /* specific case : if transport stream output to RTP,
                   we use a raw transport stream reader */
3661
                av_dict_set(&stream->in_opts, "mpeg2ts_compute_pcr", "1", 0);
3662
            }
3663

3664
            http_log("Opening file '%s'\n", stream->feed_filename);
3665
            if ((ret = avformat_open_input(&infile, stream->feed_filename, stream->ifmt, &stream->in_opts)) < 0) {
3666
                http_log("Could not open '%s': %d\n", stream->feed_filename, ret);
3667 3668 3669 3670 3671 3672
                /* 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' */
3673
                if (avformat_find_stream_info(infile, NULL) < 0) {
3674
                    http_log("Could not find codec parameters from '%s'\n",
3675
                             stream->feed_filename);
3676
                    avformat_close_input(&infile);
3677 3678
                    goto fail;
                }
3679 3680
                extract_mpeg4_header(infile);

3681
                for(i=0;i<infile->nb_streams;i++)
3682
                    add_av_stream1(stream, infile->streams[i]->codec, 1);
3683

3684
                avformat_close_input(&infile);
3685 3686 3687 3688 3689
            }
        }
    }
}

Fabrice Bellard's avatar
Fabrice Bellard committed
3690
/* compute the needed AVStream for each feed */
3691
static void build_feed_streams(void)
Fabrice Bellard's avatar
Fabrice Bellard committed
3692 3693 3694 3695
{
    FFStream *stream, *feed;
    int i;

3696 3697 3698 3699 3700
    /* gather all streams */
    for(stream = first_stream; stream != NULL; stream = stream->next) {
        feed = stream->feed;
        if (feed) {
            if (stream->is_feed) {
3701
                for(i=0;i<stream->nb_streams;i++)
Fabrice Bellard's avatar
Fabrice Bellard committed
3702
                    stream->feed_streams[i] = i;
3703 3704 3705 3706
            } 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
3707 3708 3709 3710 3711 3712 3713 3714
            }
        }
    }

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

3715
        if (avio_check(feed->feed_filename, AVIO_FLAG_READ) > 0) {
3716
            /* See if it matches */
3717
            AVFormatContext *s = NULL;
3718 3719
            int matches = 0;

3720
            if (avformat_open_input(&s, feed->feed_filename, NULL, NULL) >= 0) {
3721 3722
                /* set buffer size */
                ffio_set_buf_size(s->pb, FFM_PACKET_SIZE);
3723 3724 3725 3726 3727 3728 3729 3730 3731 3732
                /* 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) {
3733
                            http_log("Index & Id do not match for stream %d (%s)\n",
3734
                                   i, feed->feed_filename);
3735 3736 3737 3738
                            matches = 0;
                        } else {
                            AVCodecContext *ccf, *ccs;

3739 3740
                            ccf = sf->codec;
                            ccs = ss->codec;
3741 3742
#define CHECK_CODEC(x)  (ccf->x != ccs->x)

3743
                            if (CHECK_CODEC(codec_id) || CHECK_CODEC(codec_type)) {
3744
                                http_log("Codecs do not match for stream %d\n", i);
3745 3746
                                matches = 0;
                            } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3747
                                http_log("Codec bitrates do not match for stream %d\n", i);
3748
                                matches = 0;
3749
                            } else if (ccf->codec_type == AVMEDIA_TYPE_VIDEO) {
3750 3751
                                if (CHECK_CODEC(time_base.den) ||
                                    CHECK_CODEC(time_base.num) ||
3752 3753
                                    CHECK_CODEC(width) ||
                                    CHECK_CODEC(height)) {
3754
                                    http_log("Codec width, height and framerate do not match for stream %d\n", i);
3755 3756
                                    matches = 0;
                                }
3757
                            } else if (ccf->codec_type == AVMEDIA_TYPE_AUDIO) {
3758 3759 3760
                                if (CHECK_CODEC(sample_rate) ||
                                    CHECK_CODEC(channels) ||
                                    CHECK_CODEC(frame_size)) {
3761
                                    http_log("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3762 3763 3764
                                    matches = 0;
                                }
                            } else {
3765
                                http_log("Unknown codec type\n");
3766 3767 3768
                                matches = 0;
                            }
                        }
3769
                        if (!matches)
3770 3771
                            break;
                    }
3772
                } else
3773
                    http_log("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3774 3775
                        feed->feed_filename, s->nb_streams, feed->nb_streams);

3776
                avformat_close_input(&s);
3777
            } else
3778
                http_log("Deleting feed file '%s' as it appears to be corrupt\n",
3779
                        feed->feed_filename);
3780

3781 3782
            if (!matches) {
                if (feed->readonly) {
3783
                    http_log("Unable to delete feed file '%s' as it is marked readonly\n",
3784 3785 3786
                        feed->feed_filename);
                    exit(1);
                }
3787
                unlink(feed->feed_filename);
3788
            }
3789
        }
3790
        if (avio_check(feed->feed_filename, AVIO_FLAG_WRITE) <= 0) {
3791
            AVFormatContext s1 = {0}, *s = &s1;
Fabrice Bellard's avatar
Fabrice Bellard committed
3792

3793
            if (feed->readonly) {
3794
                http_log("Unable to create feed file '%s' as it is marked readonly\n",
3795 3796 3797 3798
                    feed->feed_filename);
                exit(1);
            }

Fabrice Bellard's avatar
Fabrice Bellard committed
3799
            /* only write the header of the ffm file */
3800
            if (avio_open(&s->pb, feed->feed_filename, AVIO_FLAG_WRITE) < 0) {
3801 3802
                http_log("Could not open output feed file '%s'\n",
                         feed->feed_filename);
Fabrice Bellard's avatar
Fabrice Bellard committed
3803 3804
                exit(1);
            }
3805
            s->oformat = feed->fmt;
Fabrice Bellard's avatar
Fabrice Bellard committed
3806
            s->nb_streams = feed->nb_streams;
Mike William's avatar
Mike William committed
3807
            s->streams = feed->streams;
3808
            if (avformat_write_header(s, NULL) < 0) {
3809
                http_log("Container doesn't support the required parameters\n");
3810 3811
                exit(1);
            }
3812 3813
            /* XXX: need better api */
            av_freep(&s->priv_data);
3814
            avio_close(s->pb);
Fabrice Bellard's avatar
Fabrice Bellard committed
3815 3816 3817 3818
        }
        /* get feed size and write index */
        fd = open(feed->feed_filename, O_RDONLY);
        if (fd < 0) {
3819
            http_log("Could not open output feed file '%s'\n",
Fabrice Bellard's avatar
Fabrice Bellard committed
3820 3821 3822 3823
                    feed->feed_filename);
            exit(1);
        }

3824
        feed->feed_write_index = FFMAX(ffm_read_write_index(fd), FFM_PACKET_SIZE);
Fabrice Bellard's avatar
Fabrice Bellard committed
3825 3826
        feed->feed_size = lseek(fd, 0, SEEK_END);
        /* ensure that we do not wrap before the end of file */
3827
        if (feed->feed_max_size && feed->feed_max_size < feed->feed_size)
Fabrice Bellard's avatar
Fabrice Bellard committed
3828 3829 3830 3831 3832 3833
            feed->feed_max_size = feed->feed_size;

        close(fd);
    }
}

3834 3835 3836
/* compute the bandwidth used by each stream */
static void compute_bandwidth(void)
{
3837 3838
    unsigned bandwidth;
    int i;
3839
    FFStream *stream;
3840

3841 3842 3843 3844
    for(stream = first_stream; stream != NULL; stream = stream->next) {
        bandwidth = 0;
        for(i=0;i<stream->nb_streams;i++) {
            AVStream *st = stream->streams[i];
3845
            switch(st->codec->codec_type) {
3846 3847
            case AVMEDIA_TYPE_AUDIO:
            case AVMEDIA_TYPE_VIDEO:
3848
                bandwidth += st->codec->bit_rate;
3849 3850 3851 3852 3853 3854 3855 3856 3857
                break;
            default:
                break;
            }
        }
        stream->bandwidth = (bandwidth + 999) / 1000;
    }
}

Fabrice Bellard's avatar
Fabrice Bellard committed
3858
/* add a codec and set the default parameters */
3859
static void add_codec(FFStream *stream, AVCodecContext *av)
Fabrice Bellard's avatar
Fabrice Bellard committed
3860 3861 3862
{
    AVStream *st;

3863
    if(stream->nb_streams >= FF_ARRAY_ELEMS(stream->streams))
3864
        return;
3865

Fabrice Bellard's avatar
Fabrice Bellard committed
3866 3867
    /* compute default parameters */
    switch(av->codec_type) {
3868
    case AVMEDIA_TYPE_AUDIO:
Fabrice Bellard's avatar
Fabrice Bellard committed
3869 3870 3871 3872 3873 3874 3875
        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;
3876
    case AVMEDIA_TYPE_VIDEO:
Fabrice Bellard's avatar
Fabrice Bellard committed
3877 3878
        if (av->bit_rate == 0)
            av->bit_rate = 64000;
3879 3880 3881
        if (av->time_base.num == 0){
            av->time_base.den = 5;
            av->time_base.num = 1;
3882
        }
Fabrice Bellard's avatar
Fabrice Bellard committed
3883 3884 3885 3886
        if (av->width == 0 || av->height == 0) {
            av->width = 160;
            av->height = 128;
        }
3887
        /* Bitrate tolerance is less for streaming */
3888
        if (av->bit_rate_tolerance == 0)
3889 3890
            av->bit_rate_tolerance = FFMAX(av->bit_rate / 4,
                      (int64_t)av->bit_rate*av->time_base.num/av->time_base.den);
3891 3892 3893 3894 3895 3896
        if (av->qmin == 0)
            av->qmin = 3;
        if (av->qmax == 0)
            av->qmax = 31;
        if (av->max_qdiff == 0)
            av->max_qdiff = 3;
3897 3898
        av->qcompress = 0.5;
        av->qblur = 0.5;
3899

3900
        if (!av->nsse_weight)
3901 3902 3903
            av->nsse_weight = 8;

        av->frame_skip_cmp = FF_CMP_DCTMAX;
3904
        if (!av->me_method)
Martin Storsjö's avatar
Martin Storsjö committed
3905
            av->me_method = ME_EPZS;
3906 3907
        av->rc_buffer_aggressivity = 1.0;

3908 3909 3910
        if (!av->rc_eq)
            av->rc_eq = "tex^qComp";
        if (!av->i_quant_factor)
3911
            av->i_quant_factor = -0.8;
3912 3913 3914 3915
        if (!av->b_quant_factor)
            av->b_quant_factor = 1.25;
        if (!av->b_quant_offset)
            av->b_quant_offset = 1.25;
3916 3917
        if (!av->rc_max_rate)
            av->rc_max_rate = av->bit_rate * 2;
3918

3919 3920 3921 3922 3923
        if (av->rc_max_rate && !av->rc_buffer_size) {
            av->rc_buffer_size = av->rc_max_rate;
        }


Fabrice Bellard's avatar
Fabrice Bellard committed
3924
        break;
3925
    default:
3926
        abort();
Fabrice Bellard's avatar
Fabrice Bellard committed
3927 3928 3929 3930 3931
    }

    st = av_mallocz(sizeof(AVStream));
    if (!st)
        return;
3932
    st->codec = avcodec_alloc_context3(NULL);
Fabrice Bellard's avatar
Fabrice Bellard committed
3933
    stream->streams[stream->nb_streams++] = st;
3934
    memcpy(st->codec, av, sizeof(AVCodecContext));
Fabrice Bellard's avatar
Fabrice Bellard committed
3935 3936
}

3937
static enum AVCodecID opt_audio_codec(const char *arg)
3938
{
3939
    AVCodec *p= avcodec_find_encoder_by_name(arg);
3940

3941
    if (p == NULL || p->type != AVMEDIA_TYPE_AUDIO)
3942
        return AV_CODEC_ID_NONE;
3943 3944 3945 3946

    return p->id;
}

3947
static enum AVCodecID opt_video_codec(const char *arg)
3948
{
3949
    AVCodec *p= avcodec_find_encoder_by_name(arg);
3950

3951
    if (p == NULL || p->type != AVMEDIA_TYPE_VIDEO)
3952
        return AV_CODEC_ID_NONE;
3953 3954 3955 3956

    return p->id;
}

3957 3958
/* simplistic plugin support */

3959
#if HAVE_DLOPEN
3960
static void load_module(const char *filename)
3961 3962 3963 3964 3965 3966 3967 3968 3969
{
    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;
    }
3970

3971 3972
    init_func = dlsym(dll, "ffserver_module_init");
    if (!init_func) {
3973
        fprintf(stderr,
3974 3975 3976 3977 3978 3979 3980
                "%s: init function 'ffserver_module_init()' not found\n",
                filename);
        dlclose(dll);
    }

    init_func();
}
3981
#endif
3982

3983
static int ffserver_opt_default(const char *opt, const char *arg,
3984 3985
                       AVCodecContext *avctx, int type)
{
3986
    int ret = 0;
3987
    const AVOption *o = av_opt_find(avctx, opt, NULL, type, 0);
3988
    if(o)
3989
        ret = av_opt_set(avctx, opt, arg, 0);
3990
    return ret;
3991 3992
}

3993 3994
static int ffserver_opt_preset(const char *arg,
                       AVCodecContext *avctx, int type,
3995
                       enum AVCodecID *audio_id, enum AVCodecID *video_id)
3996 3997 3998
{
    FILE *f=NULL;
    char filename[1000], tmp[1000], tmp2[1000], line[1000];
3999 4000
    int ret = 0;
    AVCodec *codec = avcodec_find_encoder(avctx->codec_id);
4001

4002 4003
    if (!(f = get_preset_file(filename, sizeof(filename), arg, 0,
                              codec ? codec->name : NULL))) {
4004 4005 4006 4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 4017 4018 4019 4020 4021 4022 4023 4024 4025 4026 4027 4028 4029 4030 4031 4032 4033 4034 4035
        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;
}

4036 4037 4038
static AVOutputFormat *ffserver_guess_format(const char *short_name, const char *filename,
                                             const char *mime_type)
{
4039
    AVOutputFormat *fmt = av_guess_format(short_name, filename, mime_type);
4040 4041 4042 4043 4044 4045

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

        snprintf(stream_format_name, sizeof(stream_format_name), "%s_stream", fmt->name);
4046
        stream_fmt = av_guess_format(stream_format_name, NULL, NULL);
4047 4048 4049 4050 4051 4052 4053 4054

        if (stream_fmt)
            fmt = stream_fmt;
    }

    return fmt;
}

4055 4056 4057 4058 4059 4060 4061 4062 4063 4064 4065
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)++;
}

4066
static int parse_ffconfig(const char *filename)
Fabrice Bellard's avatar
Fabrice Bellard committed
4067 4068 4069 4070 4071 4072 4073
{
    FILE *f;
    char line[1024];
    char cmd[64];
    char arg[1024];
    const char *p;
    int val, errors, line_num;
4074
    FFStream **last_stream, *stream, *redirect;
4075
    FFStream **last_feed, *feed, *s;
Fabrice Bellard's avatar
Fabrice Bellard committed
4076
    AVCodecContext audio_enc, video_enc;
4077
    enum AVCodecID audio_id, video_id;
Fabrice Bellard's avatar
Fabrice Bellard committed
4078 4079 4080 4081 4082 4083

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

Fabrice Bellard's avatar
Fabrice Bellard committed
4085 4086 4087 4088 4089 4090 4091 4092
    errors = 0;
    line_num = 0;
    first_stream = NULL;
    last_stream = &first_stream;
    first_feed = NULL;
    last_feed = &first_feed;
    stream = NULL;
    feed = NULL;
4093
    redirect = NULL;
4094 4095
    audio_id = AV_CODEC_ID_NONE;
    video_id = AV_CODEC_ID_NONE;
4096 4097

#define ERROR(...) report_config_error(filename, line_num, &errors, __VA_ARGS__)
Fabrice Bellard's avatar
Fabrice Bellard committed
4098 4099 4100 4101 4102
    for(;;) {
        if (fgets(line, sizeof(line), f) == NULL)
            break;
        line_num++;
        p = line;
4103
        while (av_isspace(*p))
Fabrice Bellard's avatar
Fabrice Bellard committed
4104 4105 4106 4107 4108
            p++;
        if (*p == '\0' || *p == '#')
            continue;

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

4110
        if (!av_strcasecmp(cmd, "Port")) {
Fabrice Bellard's avatar
Fabrice Bellard committed
4111
            get_arg(arg, sizeof(arg), &p);
4112 4113
            val = atoi(arg);
            if (val < 1 || val > 65536) {
4114
                ERROR("Invalid_port: %s\n", arg);
4115 4116
            }
            my_http_addr.sin_port = htons(val);
4117
        } else if (!av_strcasecmp(cmd, "BindAddress")) {
Fabrice Bellard's avatar
Fabrice Bellard committed
4118
            get_arg(arg, sizeof(arg), &p);
4119
            if (resolve_host(&my_http_addr.sin_addr, arg) != 0) {
4120
                ERROR("%s:%d: Invalid host/IP address: %s\n", arg);
4121
            }
4122 4123
        } else if (!av_strcasecmp(cmd, "NoDaemon")) {
            // do nothing here, its the default now
4124
        } else if (!av_strcasecmp(cmd, "RTSPPort")) {
4125
            get_arg(arg, sizeof(arg), &p);
4126 4127
            val = atoi(arg);
            if (val < 1 || val > 65536) {
4128
                ERROR("%s:%d: Invalid port: %s\n", arg);
4129 4130
            }
            my_rtsp_addr.sin_port = htons(atoi(arg));
4131
        } else if (!av_strcasecmp(cmd, "RTSPBindAddress")) {
4132
            get_arg(arg, sizeof(arg), &p);
4133
            if (resolve_host(&my_rtsp_addr.sin_addr, arg) != 0) {
4134
                ERROR("Invalid host/IP address: %s\n", arg);
Fabrice Bellard's avatar
Fabrice Bellard committed
4135
            }
4136
        } else if (!av_strcasecmp(cmd, "MaxHTTPConnections")) {
4137 4138 4139
            get_arg(arg, sizeof(arg), &p);
            val = atoi(arg);
            if (val < 1 || val > 65536) {
4140
                ERROR("Invalid MaxHTTPConnections: %s\n", arg);
4141 4142
            }
            nb_max_http_connections = val;
4143
        } else if (!av_strcasecmp(cmd, "MaxClients")) {
Fabrice Bellard's avatar
Fabrice Bellard committed
4144 4145
            get_arg(arg, sizeof(arg), &p);
            val = atoi(arg);
4146
            if (val < 1 || val > nb_max_http_connections) {
4147
                ERROR("Invalid MaxClients: %s\n", arg);
Fabrice Bellard's avatar
Fabrice Bellard committed
4148 4149 4150
            } else {
                nb_max_connections = val;
            }
4151
        } else if (!av_strcasecmp(cmd, "MaxBandwidth")) {
4152
            int64_t llval;
4153
            get_arg(arg, sizeof(arg), &p);
4154 4155
            llval = atoll(arg);
            if (llval < 10 || llval > 10000000) {
4156
                ERROR("Invalid MaxBandwidth: %s\n", arg);
4157
            } else
4158
                max_bandwidth = llval;
4159
        } else if (!av_strcasecmp(cmd, "CustomLog")) {
4160 4161
            if (!ffserver_debug)
                get_arg(logfilename, sizeof(logfilename), &p);
4162
        } else if (!av_strcasecmp(cmd, "<Feed")) {
Fabrice Bellard's avatar
Fabrice Bellard committed
4163 4164 4165 4166
            /*********************************************/
            /* Feed related options */
            char *q;
            if (stream || feed) {
4167
                ERROR("Already in a tag\n");
Fabrice Bellard's avatar
Fabrice Bellard committed
4168 4169 4170 4171 4172 4173
            } else {
                feed = av_mallocz(sizeof(FFStream));
                get_arg(feed->filename, sizeof(feed->filename), &p);
                q = strrchr(feed->filename, '>');
                if (*q)
                    *q = '\0';
4174 4175 4176

                for (s = first_feed; s; s = s->next) {
                    if (!strcmp(feed->filename, s->filename)) {
4177
                        ERROR("Feed '%s' already registered\n", s->filename);
4178 4179 4180
                    }
                }

4181
                feed->fmt = av_guess_format("ffm", NULL, NULL);
Fabrice Bellard's avatar
Fabrice Bellard committed
4182 4183 4184 4185 4186 4187
                /* 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 :-) */
4188 4189 4190 4191 4192 4193 4194

                /* 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
4195
            }
4196
        } else if (!av_strcasecmp(cmd, "Launch")) {
4197 4198 4199
            if (feed) {
                int i;

4200
                feed->child_argv = av_mallocz(64 * sizeof(char *));
4201

4202
                for (i = 0; i < 62; i++) {
Alex Beregszaszi's avatar
Alex Beregszaszi committed
4203 4204
                    get_arg(arg, sizeof(arg), &p);
                    if (!arg[0])
4205 4206
                        break;

Alex Beregszaszi's avatar
Alex Beregszaszi committed
4207
                    feed->child_argv[i] = av_strdup(arg);
4208 4209
                }

4210
                feed->child_argv[i] = av_asprintf("http://%s:%d/%s",
4211 4212 4213
                        (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);
4214
            }
4215
        } else if (!av_strcasecmp(cmd, "ReadOnlyFile")) {
4216 4217 4218 4219 4220 4221
            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);
            }
4222
        } else if (!av_strcasecmp(cmd, "File")) {
Fabrice Bellard's avatar
Fabrice Bellard committed
4223 4224
            if (feed) {
                get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
4225
            } else if (stream)
Fabrice Bellard's avatar
Fabrice Bellard committed
4226
                get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4227
        } else if (!av_strcasecmp(cmd, "Truncate")) {
4228 4229 4230 4231
            if (feed) {
                get_arg(arg, sizeof(arg), &p);
                feed->truncate = strtod(arg, NULL);
            }
4232
        } else if (!av_strcasecmp(cmd, "FileMaxSize")) {
Fabrice Bellard's avatar
Fabrice Bellard committed
4233
            if (feed) {
4234
                char *p1;
Fabrice Bellard's avatar
Fabrice Bellard committed
4235 4236 4237 4238
                double fsize;

                get_arg(arg, sizeof(arg), &p);
                p1 = arg;
4239
                fsize = strtod(p1, &p1);
4240
                switch(av_toupper(*p1)) {
Fabrice Bellard's avatar
Fabrice Bellard committed
4241 4242 4243 4244 4245 4246 4247 4248 4249 4250
                case 'K':
                    fsize *= 1024;
                    break;
                case 'M':
                    fsize *= 1024 * 1024;
                    break;
                case 'G':
                    fsize *= 1024 * 1024 * 1024;
                    break;
                }
4251
                feed->feed_max_size = (int64_t)fsize;
4252
                if (feed->feed_max_size < FFM_PACKET_SIZE*4) {
4253
                    ERROR("Feed max file size is too small, must be at least %d\n", FFM_PACKET_SIZE*4);
4254
                }
Fabrice Bellard's avatar
Fabrice Bellard committed
4255
            }
4256
        } else if (!av_strcasecmp(cmd, "</Feed>")) {
Fabrice Bellard's avatar
Fabrice Bellard committed
4257
            if (!feed) {
4258
                ERROR("No corresponding <Feed> for </Feed>\n");
Fabrice Bellard's avatar
Fabrice Bellard committed
4259 4260
            }
            feed = NULL;
4261
        } else if (!av_strcasecmp(cmd, "<Stream")) {
Fabrice Bellard's avatar
Fabrice Bellard committed
4262 4263 4264 4265
            /*********************************************/
            /* Stream related options */
            char *q;
            if (stream || feed) {
4266
                ERROR("Already in a tag\n");
Fabrice Bellard's avatar
Fabrice Bellard committed
4267
            } else {
4268
                FFStream *s;
Fabrice Bellard's avatar
Fabrice Bellard committed
4269 4270 4271
                stream = av_mallocz(sizeof(FFStream));
                get_arg(stream->filename, sizeof(stream->filename), &p);
                q = strrchr(stream->filename, '>');
4272
                if (q)
Fabrice Bellard's avatar
Fabrice Bellard committed
4273
                    *q = '\0';
4274 4275 4276

                for (s = first_stream; s; s = s->next) {
                    if (!strcmp(stream->filename, s->filename)) {
4277
                        ERROR("Stream '%s' already registered\n", s->filename);
4278 4279 4280
                    }
                }

4281
                stream->fmt = ffserver_guess_format(NULL, stream->filename, NULL);
4282 4283
                avcodec_get_context_defaults3(&video_enc, NULL);
                avcodec_get_context_defaults3(&audio_enc, NULL);
4284

4285 4286
                audio_id = AV_CODEC_ID_NONE;
                video_id = AV_CODEC_ID_NONE;
Fabrice Bellard's avatar
Fabrice Bellard committed
4287 4288 4289 4290
                if (stream->fmt) {
                    audio_id = stream->fmt->audio_codec;
                    video_id = stream->fmt->video_codec;
                }
4291 4292 4293

                *last_stream = stream;
                last_stream = &stream->next;
Fabrice Bellard's avatar
Fabrice Bellard committed
4294
            }
4295
        } else if (!av_strcasecmp(cmd, "Feed")) {
Fabrice Bellard's avatar
Fabrice Bellard committed
4296 4297 4298
            get_arg(arg, sizeof(arg), &p);
            if (stream) {
                FFStream *sfeed;
4299

Fabrice Bellard's avatar
Fabrice Bellard committed
4300 4301 4302 4303 4304 4305
                sfeed = first_feed;
                while (sfeed != NULL) {
                    if (!strcmp(sfeed->filename, arg))
                        break;
                    sfeed = sfeed->next_feed;
                }
4306
                if (!sfeed)
4307
                    ERROR("feed '%s' not defined\n", arg);
4308
                else
Fabrice Bellard's avatar
Fabrice Bellard committed
4309 4310
                    stream->feed = sfeed;
            }
4311
        } else if (!av_strcasecmp(cmd, "Format")) {
Fabrice Bellard's avatar
Fabrice Bellard committed
4312
            get_arg(arg, sizeof(arg), &p);
4313
            if (stream) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
4314 4315 4316 4317 4318 4319 4320 4321
                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");
4322
                    stream->fmt = ffserver_guess_format(arg, NULL, NULL);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
4323
                    if (!stream->fmt) {
4324
                        ERROR("Unknown Format: %s\n", arg);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
4325 4326 4327 4328 4329
                    }
                }
                if (stream->fmt) {
                    audio_id = stream->fmt->audio_codec;
                    video_id = stream->fmt->video_codec;
Fabrice Bellard's avatar
Fabrice Bellard committed
4330
                }
4331
            }
4332
        } else if (!av_strcasecmp(cmd, "InputFormat")) {
4333
            get_arg(arg, sizeof(arg), &p);
4334
            if (stream) {
4335 4336
                stream->ifmt = av_find_input_format(arg);
                if (!stream->ifmt) {
4337
                    ERROR("Unknown input format: %s\n", arg);
4338
                }
4339
            }
4340
        } else if (!av_strcasecmp(cmd, "FaviconURL")) {
4341 4342 4343
            if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
                get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
            } else {
4344
                ERROR("FaviconURL only permitted for status streams\n");
4345
            }
4346
        } else if (!av_strcasecmp(cmd, "Author")) {
4347
            if (stream)
4348
                get_arg(stream->author, sizeof(stream->author), &p);
4349
        } else if (!av_strcasecmp(cmd, "Comment")) {
4350
            if (stream)
4351
                get_arg(stream->comment, sizeof(stream->comment), &p);
4352
        } else if (!av_strcasecmp(cmd, "Copyright")) {
4353
            if (stream)
4354
                get_arg(stream->copyright, sizeof(stream->copyright), &p);
4355
        } else if (!av_strcasecmp(cmd, "Title")) {
4356
            if (stream)
4357
                get_arg(stream->title, sizeof(stream->title), &p);
4358
        } else if (!av_strcasecmp(cmd, "Preroll")) {
4359
            get_arg(arg, sizeof(arg), &p);
4360
            if (stream)
4361
                stream->prebuffer = atof(arg) * 1000;
4362
        } else if (!av_strcasecmp(cmd, "StartSendOnKey")) {
4363
            if (stream)
4364
                stream->send_on_key = 1;
4365
        } else if (!av_strcasecmp(cmd, "AudioCodec")) {
4366 4367
            get_arg(arg, sizeof(arg), &p);
            audio_id = opt_audio_codec(arg);
4368
            if (audio_id == AV_CODEC_ID_NONE) {
4369
                ERROR("Unknown AudioCodec: %s\n", arg);
4370
            }
4371
        } else if (!av_strcasecmp(cmd, "VideoCodec")) {
4372 4373
            get_arg(arg, sizeof(arg), &p);
            video_id = opt_video_codec(arg);
4374
            if (video_id == AV_CODEC_ID_NONE) {
4375
                ERROR("Unknown VideoCodec: %s\n", arg);
4376
            }
4377
        } else if (!av_strcasecmp(cmd, "MaxTime")) {
4378
            get_arg(arg, sizeof(arg), &p);
4379
            if (stream)
4380
                stream->max_time = atof(arg) * 1000;
4381
        } else if (!av_strcasecmp(cmd, "AudioBitRate")) {
Fabrice Bellard's avatar
Fabrice Bellard committed
4382
            get_arg(arg, sizeof(arg), &p);
4383
            if (stream)
4384
                audio_enc.bit_rate = lrintf(atof(arg) * 1000);
4385
        } else if (!av_strcasecmp(cmd, "AudioChannels")) {
Fabrice Bellard's avatar
Fabrice Bellard committed
4386
            get_arg(arg, sizeof(arg), &p);
4387
            if (stream)
Fabrice Bellard's avatar
Fabrice Bellard committed
4388
                audio_enc.channels = atoi(arg);
4389
        } else if (!av_strcasecmp(cmd, "AudioSampleRate")) {
Fabrice Bellard's avatar
Fabrice Bellard committed
4390
            get_arg(arg, sizeof(arg), &p);
4391
            if (stream)
Fabrice Bellard's avatar
Fabrice Bellard committed
4392
                audio_enc.sample_rate = atoi(arg);
4393
        } else if (!av_strcasecmp(cmd, "AudioQuality")) {
4394
            get_arg(arg, sizeof(arg), &p);
4395
            if (stream) {
Michael Niedermayer's avatar
Michael Niedermayer committed
4396
//                audio_enc.quality = atof(arg) * 1000;
4397
            }
4398
        } else if (!av_strcasecmp(cmd, "VideoBitRateRange")) {
4399 4400 4401 4402 4403 4404 4405 4406 4407
            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 {
4408
                    ERROR("Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n", arg);
4409 4410
                }
            }
4411
        } else if (!av_strcasecmp(cmd, "Debug")) {
4412 4413 4414 4415
            if (stream) {
                get_arg(arg, sizeof(arg), &p);
                video_enc.debug = strtol(arg,0,0);
            }
4416
        } else if (!av_strcasecmp(cmd, "Strict")) {
4417 4418 4419 4420
            if (stream) {
                get_arg(arg, sizeof(arg), &p);
                video_enc.strict_std_compliance = atoi(arg);
            }
4421
        } else if (!av_strcasecmp(cmd, "VideoBufferSize")) {
4422 4423
            if (stream) {
                get_arg(arg, sizeof(arg), &p);
4424
                video_enc.rc_buffer_size = atoi(arg) * 8*1024;
4425
            }
4426
        } else if (!av_strcasecmp(cmd, "VideoBitRateTolerance")) {
4427 4428 4429 4430
            if (stream) {
                get_arg(arg, sizeof(arg), &p);
                video_enc.bit_rate_tolerance = atoi(arg) * 1000;
            }
4431
        } else if (!av_strcasecmp(cmd, "VideoBitRate")) {
Fabrice Bellard's avatar
Fabrice Bellard committed
4432 4433 4434 4435
            get_arg(arg, sizeof(arg), &p);
            if (stream) {
                video_enc.bit_rate = atoi(arg) * 1000;
            }
4436
        } else if (!av_strcasecmp(cmd, "VideoSize")) {
Fabrice Bellard's avatar
Fabrice Bellard committed
4437 4438
            get_arg(arg, sizeof(arg), &p);
            if (stream) {
4439
                av_parse_video_size(&video_enc.width, &video_enc.height, arg);
Fabrice Bellard's avatar
Fabrice Bellard committed
4440 4441
                if ((video_enc.width % 16) != 0 ||
                    (video_enc.height % 16) != 0) {
4442
                    ERROR("Image size must be a multiple of 16\n");
Fabrice Bellard's avatar
Fabrice Bellard committed
4443 4444
                }
            }
4445
        } else if (!av_strcasecmp(cmd, "VideoFrameRate")) {
Fabrice Bellard's avatar
Fabrice Bellard committed
4446 4447
            get_arg(arg, sizeof(arg), &p);
            if (stream) {
4448
                AVRational frame_rate;
4449
                if (av_parse_video_rate(&frame_rate, arg) < 0) {
4450
                    ERROR("Incorrect frame rate: %s\n", arg);
4451 4452 4453 4454
                } else {
                    video_enc.time_base.num = frame_rate.den;
                    video_enc.time_base.den = frame_rate.num;
                }
Fabrice Bellard's avatar
Fabrice Bellard committed
4455
            }
4456
        } else if (!av_strcasecmp(cmd, "VideoGopSize")) {
Fabrice Bellard's avatar
Fabrice Bellard committed
4457
            get_arg(arg, sizeof(arg), &p);
4458
            if (stream)
Fabrice Bellard's avatar
Fabrice Bellard committed
4459
                video_enc.gop_size = atoi(arg);
4460
        } else if (!av_strcasecmp(cmd, "VideoIntraOnly")) {
4461
            if (stream)
Fabrice Bellard's avatar
Fabrice Bellard committed
4462
                video_enc.gop_size = 1;
4463
        } else if (!av_strcasecmp(cmd, "VideoHighQuality")) {
4464
            if (stream)
4465
                video_enc.mb_decision = FF_MB_DECISION_BITS;
4466
        } else if (!av_strcasecmp(cmd, "Video4MotionVector")) {
4467
            if (stream) {
4468
                video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove
4469 4470
                video_enc.flags |= CODEC_FLAG_4MV;
            }
4471 4472
        } else if (!av_strcasecmp(cmd, "AVOptionVideo") ||
                   !av_strcasecmp(cmd, "AVOptionAudio")) {
4473 4474 4475 4476 4477
            char arg2[1024];
            AVCodecContext *avctx;
            int type;
            get_arg(arg, sizeof(arg), &p);
            get_arg(arg2, sizeof(arg2), &p);
4478
            if (!av_strcasecmp(cmd, "AVOptionVideo")) {
4479 4480 4481 4482 4483 4484
                avctx = &video_enc;
                type = AV_OPT_FLAG_VIDEO_PARAM;
            } else {
                avctx = &audio_enc;
                type = AV_OPT_FLAG_AUDIO_PARAM;
            }
4485
            if (ffserver_opt_default(arg, arg2, avctx, type|AV_OPT_FLAG_ENCODING_PARAM)) {
4486
                ERROR("AVOption error: %s %s\n", arg, arg2);
4487
            }
4488 4489
        } else if (!av_strcasecmp(cmd, "AVPresetVideo") ||
                   !av_strcasecmp(cmd, "AVPresetAudio")) {
4490 4491 4492
            AVCodecContext *avctx;
            int type;
            get_arg(arg, sizeof(arg), &p);
4493
            if (!av_strcasecmp(cmd, "AVPresetVideo")) {
4494 4495 4496 4497 4498 4499 4500 4501 4502 4503 4504
                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);
            }
4505
        } else if (!av_strcasecmp(cmd, "VideoTag")) {
4506
            get_arg(arg, sizeof(arg), &p);
4507
            if ((strlen(arg) == 4) && stream)
4508
                video_enc.codec_tag = MKTAG(arg[0], arg[1], arg[2], arg[3]);
4509
        } else if (!av_strcasecmp(cmd, "BitExact")) {
4510
            if (stream)
4511
                video_enc.flags |= CODEC_FLAG_BITEXACT;
4512
        } else if (!av_strcasecmp(cmd, "DctFastint")) {
4513
            if (stream)
4514
                video_enc.dct_algo  = FF_DCT_FASTINT;
4515
        } else if (!av_strcasecmp(cmd, "IdctSimple")) {
4516
            if (stream)
4517
                video_enc.idct_algo = FF_IDCT_SIMPLE;
4518
        } else if (!av_strcasecmp(cmd, "Qscale")) {
4519 4520 4521 4522 4523
            get_arg(arg, sizeof(arg), &p);
            if (stream) {
                video_enc.flags |= CODEC_FLAG_QSCALE;
                video_enc.global_quality = FF_QP2LAMBDA * atoi(arg);
            }
4524
        } else if (!av_strcasecmp(cmd, "VideoQDiff")) {
4525
            get_arg(arg, sizeof(arg), &p);
4526 4527 4528
            if (stream) {
                video_enc.max_qdiff = atoi(arg);
                if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
4529
                    ERROR("VideoQDiff out of range\n");
4530 4531
                }
            }
4532
        } else if (!av_strcasecmp(cmd, "VideoQMax")) {
4533
            get_arg(arg, sizeof(arg), &p);
4534 4535 4536
            if (stream) {
                video_enc.qmax = atoi(arg);
                if (video_enc.qmax < 1 || video_enc.qmax > 31) {
4537
                    ERROR("VideoQMax out of range\n");
4538 4539
                }
            }
4540
        } else if (!av_strcasecmp(cmd, "VideoQMin")) {
4541
            get_arg(arg, sizeof(arg), &p);
4542 4543 4544
            if (stream) {
                video_enc.qmin = atoi(arg);
                if (video_enc.qmin < 1 || video_enc.qmin > 31) {
4545
                    ERROR("VideoQMin out of range\n");
4546 4547
                }
            }
4548
        } else if (!av_strcasecmp(cmd, "LumiMask")) {
4549
            get_arg(arg, sizeof(arg), &p);
4550
            if (stream)
4551
                video_enc.lumi_masking = atof(arg);
4552
        } else if (!av_strcasecmp(cmd, "DarkMask")) {
4553
            get_arg(arg, sizeof(arg), &p);
4554
            if (stream)
4555
                video_enc.dark_masking = atof(arg);
4556
        } else if (!av_strcasecmp(cmd, "NoVideo")) {
4557
            video_id = AV_CODEC_ID_NONE;
4558
        } else if (!av_strcasecmp(cmd, "NoAudio")) {
4559
            audio_id = AV_CODEC_ID_NONE;
4560
        } else if (!av_strcasecmp(cmd, "ACL")) {
4561
            parse_acl_row(stream, feed, NULL, p, filename, line_num);
4562
        } else if (!av_strcasecmp(cmd, "DynamicACL")) {
4563 4564
            if (stream) {
                get_arg(stream->dynamic_acl, sizeof(stream->dynamic_acl), &p);
4565
            }
4566
        } else if (!av_strcasecmp(cmd, "RTSPOption")) {
4567 4568 4569
            get_arg(arg, sizeof(arg), &p);
            if (stream) {
                av_freep(&stream->rtsp_option);
Alex Beregszaszi's avatar
Alex Beregszaszi committed
4570
                stream->rtsp_option = av_strdup(arg);
4571
            }
4572
        } else if (!av_strcasecmp(cmd, "MulticastAddress")) {
4573 4574
            get_arg(arg, sizeof(arg), &p);
            if (stream) {
4575
                if (resolve_host(&stream->multicast_ip, arg) != 0) {
4576
                    ERROR("Invalid host/IP address: %s\n", arg);
4577 4578
                }
                stream->is_multicast = 1;
4579
                stream->loop = 1; /* default is looping */
4580
            }
4581
        } else if (!av_strcasecmp(cmd, "MulticastPort")) {
4582
            get_arg(arg, sizeof(arg), &p);
4583
            if (stream)
4584
                stream->multicast_port = atoi(arg);
4585
        } else if (!av_strcasecmp(cmd, "MulticastTTL")) {
4586
            get_arg(arg, sizeof(arg), &p);
4587
            if (stream)
4588
                stream->multicast_ttl = atoi(arg);
4589
        } else if (!av_strcasecmp(cmd, "NoLoop")) {
4590
            if (stream)
4591
                stream->loop = 0;
4592
        } else if (!av_strcasecmp(cmd, "</Stream>")) {
Fabrice Bellard's avatar
Fabrice Bellard committed
4593
            if (!stream) {
4594
                ERROR("No corresponding <Stream> for </Stream>\n");
4595
            } else {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
4596
                if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
4597
                    if (audio_id != AV_CODEC_ID_NONE) {
4598
                        audio_enc.codec_type = AVMEDIA_TYPE_AUDIO;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
4599 4600 4601
                        audio_enc.codec_id = audio_id;
                        add_codec(stream, &audio_enc);
                    }
4602
                    if (video_id != AV_CODEC_ID_NONE) {
4603
                        video_enc.codec_type = AVMEDIA_TYPE_VIDEO;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
4604 4605 4606
                        video_enc.codec_id = video_id;
                        add_codec(stream, &video_enc);
                    }
Fabrice Bellard's avatar
Fabrice Bellard committed
4607
                }
Baptiste Coudurier's avatar
Baptiste Coudurier committed
4608
                stream = NULL;
4609
            }
4610
        } else if (!av_strcasecmp(cmd, "<Redirect")) {
4611 4612 4613
            /*********************************************/
            char *q;
            if (stream || feed || redirect) {
4614
                ERROR("Already in a tag\n");
4615 4616 4617 4618 4619 4620 4621 4622 4623 4624 4625
            } 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;
            }
4626
        } else if (!av_strcasecmp(cmd, "URL")) {
4627
            if (redirect)
4628
                get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4629
        } else if (!av_strcasecmp(cmd, "</Redirect>")) {
4630
            if (!redirect) {
4631
                ERROR("No corresponding <Redirect> for </Redirect>\n");
4632
            } else {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
4633
                if (!redirect->feed_filename[0]) {
4634
                    ERROR("No URL found for <Redirect>\n");
Baptiste Coudurier's avatar
Baptiste Coudurier committed
4635 4636
                }
                redirect = NULL;
4637
            }
4638
        } else if (!av_strcasecmp(cmd, "LoadModule")) {
4639
            get_arg(arg, sizeof(arg), &p);
4640
#if HAVE_DLOPEN
4641
            load_module(arg);
4642
#else
4643
            ERROR("Module support not compiled into this version: '%s'\n", arg);
4644
#endif
Fabrice Bellard's avatar
Fabrice Bellard committed
4645
        } else {
4646
            ERROR("Incorrect keyword: '%s'\n", cmd);
Fabrice Bellard's avatar
Fabrice Bellard committed
4647 4648
        }
    }
4649
#undef ERROR
Fabrice Bellard's avatar
Fabrice Bellard committed
4650 4651 4652 4653 4654 4655 4656 4657

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

4658 4659 4660 4661 4662 4663 4664 4665 4666 4667 4668 4669 4670 4671 4672
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);

4673
                if (uptime < 30)
4674 4675 4676 4677 4678 4679 4680 4681 4682
                    /* Turn off any more restarts */
                    feed->child_argv = 0;
            }
        }
    }

    need_to_start_children = 1;
}

4683
static void opt_debug(void)
4684 4685
{
    ffserver_debug = 1;
4686
    logfilename[0] = '-';
4687 4688
}

4689
void show_help_default(const char *opt, const char *arg)
4690
{
4691
    printf("usage: ffserver [options]\n"
4692 4693
           "Hyper fast multi format Audio/Video streaming server\n");
    printf("\n");
4694
    show_help_options(options, "Main options:", 0, 0, 0);
4695 4696 4697
}

static const OptionDef options[] = {
4698
#include "cmdutils_common_opts.h"
4699 4700 4701 4702 4703 4704
    { "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
4705 4706
int main(int argc, char **argv)
{
4707
    struct sigaction sigact = { { 0 } };
Fabrice Bellard's avatar
Fabrice Bellard committed
4708

4709
    parse_loglevel(argc, argv, options);
4710
    av_register_all();
4711
    avformat_network_init();
Fabrice Bellard's avatar
Fabrice Bellard committed
4712

4713
    show_banner(argc, argv, options);
4714

4715
    my_program_name = argv[0];
4716

4717
    parse_options(NULL, argc, argv, options, NULL);
Fabrice Bellard's avatar
Fabrice Bellard committed
4718

4719 4720 4721
    if (!config_filename)
        config_filename = av_strdup("/etc/ffserver.conf");

4722
    unsetenv("http_proxy");             /* Kill the http_proxy */
4723

4724
    av_lfg_init(&random_state, av_get_random_seed());
4725

4726 4727 4728 4729
    sigact.sa_handler = handle_child_exit;
    sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
    sigaction(SIGCHLD, &sigact, 0);

Fabrice Bellard's avatar
Fabrice Bellard committed
4730 4731 4732 4733
    if (parse_ffconfig(config_filename) < 0) {
        fprintf(stderr, "Incorrect config file - exiting.\n");
        exit(1);
    }
4734
    av_freep(&config_filename);
Fabrice Bellard's avatar
Fabrice Bellard committed
4735

4736 4737 4738
    /* open log file if needed */
    if (logfilename[0] != '\0') {
        if (!strcmp(logfilename, "-"))
4739
            logfile = stdout;
4740 4741 4742 4743 4744
        else
            logfile = fopen(logfilename, "a");
        av_log_set_callback(http_av_log);
    }

4745 4746
    build_file_streams();

Fabrice Bellard's avatar
Fabrice Bellard committed
4747 4748
    build_feed_streams();

4749 4750
    compute_bandwidth();

Fabrice Bellard's avatar
Fabrice Bellard committed
4751 4752 4753
    /* signal init */
    signal(SIGPIPE, SIG_IGN);

4754
    if (http_server() < 0) {
4755
        http_log("Could not start server\n");
Fabrice Bellard's avatar
Fabrice Bellard committed
4756 4757 4758 4759 4760
        exit(1);
    }

    return 0;
}