ffserver.c 155 KB
Newer Older
Fabrice Bellard's avatar
Fabrice Bellard committed
1
/*
2
 * Copyright (c) 2000, 2001, 2002 Fabrice Bellard
Fabrice Bellard's avatar
Fabrice Bellard committed
3
 *
4 5 6
 * This file is part of FFmpeg.
 *
 * FFmpeg is free software; you can redistribute it and/or
7 8
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
9
 * version 2.1 of the License, or (at your option) any later version.
Fabrice Bellard's avatar
Fabrice Bellard committed
10
 *
11
 * FFmpeg is distributed in the hope that it will be useful,
Fabrice Bellard's avatar
Fabrice Bellard committed
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
Fabrice Bellard's avatar
Fabrice Bellard committed
15
 *
16
 * You should have received a copy of the GNU Lesser General Public
17
 * License along with FFmpeg; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Fabrice Bellard's avatar
Fabrice Bellard committed
19
 */
20

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

26
#include "config.h"
27
#if !HAVE_CLOSESOCKET
28 29 30 31
#define closesocket close
#endif
#include <string.h>
#include <stdlib.h>
32
#include <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

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

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

72 73
static const OptionDef options[];

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

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

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

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

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

105 106
#define MAX_STREAMS 20

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

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

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

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

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

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

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

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

182 183 184 185 186 187
    /* 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
188 189 190 191 192 193
} HTTPContext;

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

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

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

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

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

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

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

268 269 270 271
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
272

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

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

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

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

306 307
static const char *my_program_name;

308
static const char *config_filename;
309

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

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

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

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

324
static AVLFG random_state;
325

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

328 329 330 331
static int64_t ffm_read_write_index(int fd)
{
    uint8_t buf[8];

332 333
    if (lseek(fd, 8, SEEK_SET) < 0)
        return AVERROR(EIO);
334 335 336 337 338 339 340 341 342 343 344 345
    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;
346 347
    if (lseek(fd, 8, SEEK_SET) < 0)
        return AVERROR(EIO);
348 349 350 351 352 353 354 355 356 357 358 359 360
    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;
}

361 362 363 364 365 366 367 368
/* 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;
369
        struct addrinfo hints = { 0 };
370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395
        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;
}

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

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

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

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

448 449
static void log_connection(HTTPContext *c)
{
450
    if (c->suppress_log)
451 452
        return;

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

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

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

477 478 479
    return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
}

480

481 482
static void start_children(FFStream *feed)
{
483 484 485
    if (no_launch)
        return;

486
    for (; feed; feed = feed->next) {
487 488 489
        if (feed->child_argv && !feed->pid) {
            feed->pid_start = time(0);

490 491 492
            feed->pid = fork();

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

502 503 504 505 506 507 508 509 510
                av_strlcpy(pathname, my_program_name, sizeof(pathname));

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

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

517
                for (i = 3; i < 256; i++)
518
                    close(i);
519

520
                if (!ffserver_debug) {
521 522 523 524 525 526
                    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;");
527
                }
528

529 530
                signal(SIGPIPE, SIG_DFL);

531 532 533 534 535 536
                execvp(pathname, feed->child_argv);

                _exit(1);
            }
        }
    }
537 538
}

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

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

Fabrice Bellard's avatar
Fabrice Bellard committed
550 551 552
    tmp = 1;
    setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));

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

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

    return server_fd;
}

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

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

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

600
            rtp_c = rtp_new_connection(&dest_addr, stream, session_id,
601
                                       RTSP_LOWER_TRANSPORT_UDP_MULTICAST);
602
            if (!rtp_c)
603
                continue;
604

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

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

            /* change state to send data */
            rtp_c->state = HTTPSTATE_SEND_DATA;
        }
    }
}
628 629 630 631

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

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

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

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

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

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

661 662
    start_children(first_feed);

663 664
    start_multicast();

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

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

737
        cur_time = av_gettime() / 1000;
Fabrice Bellard's avatar
Fabrice Bellard committed
738

739 740 741 742 743
        if (need_to_start_children) {
            need_to_start_children = 0;
            start_children(first_feed);
        }

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

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

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

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


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

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

817 818
    if (nb_connections >= nb_max_connections) {
        http_send_too_busy_reply(fd);
819
        goto fail;
820
    }
821

822 823 824 825
    /* add a new connection */
    c = av_mallocz(sizeof(HTTPContext));
    if (!c)
        goto fail;
826

827 828 829 830 831 832 833
    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;
834 835 836

    c->next = first_http_ctx;
    first_http_ctx = c;
837
    nb_connections++;
838

839 840 841 842 843 844 845 846 847
    start_wait_request(c, is_rtsp);

    return;

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

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

869 870 871 872 873 874
    /* 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;
    }

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

    /* free RTP output streams if any */
    nb_streams = 0;
890
    if (c->stream)
891
        nb_streams = c->stream->nb_streams;
892

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

906 907
    ctx = &c->fmt_ctx;

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

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

924
    if (c->stream && !c->post && c->stream->stream_type == STREAM_TYPE_LIVE)
925
        current_bandwidth -= c->stream->bandwidth;
926 927 928 929 930 931 932

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

933
    av_freep(&c->pb_buffer);
934
    av_freep(&c->packet_buffer);
935 936 937 938 939 940 941 942
    av_free(c->buffer);
    av_free(c);
    nb_connections--;
}

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

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

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

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

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

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

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

1117 1118 1119 1120 1121
static int extract_rates(char *rates, int ratelen, const char *request)
{
    const char *p;

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

1125
            while (*q && *q != '\n' && av_isspace(*q))
1126 1127
                q++;

1128
            if (av_strncasecmp(q, "stream-switch-entry=", 20) == 0) {
1129 1130 1131 1132 1133
                int stream_no;
                int rate_no;

                q += 20;

1134
                memset(rates, 0xff, ratelen);
1135 1136 1137 1138 1139

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

1140
                    if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2)
1141
                        break;
1142

1143
                    stream_no--;
1144
                    if (stream_no < ratelen && stream_no >= 0)
1145 1146
                        rates[stream_no] = rate_no;

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

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

        p++;
    }

    return 0;
}

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

    for (i = 0; i < feed->nb_streams; i++) {
1171
        AVCodecContext *feed_codec = feed->streams[i]->codec;
1172 1173 1174 1175

        if (feed_codec->codec_id != codec->codec_id ||
            feed_codec->sample_rate != codec->sample_rate ||
            feed_codec->width != codec->width ||
1176
            feed_codec->height != codec->height)
1177 1178 1179 1180
            continue;

        /* Potential stream */

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

1207 1208 1209 1210
    /* Not much we can do for a feed */
    if (!req->feed)
        return 0;

1211
    for (i = 0; i < req->nb_streams; i++) {
1212
        AVCodecContext *codec = req->streams[i]->codec;
1213 1214 1215

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

1232 1233 1234
        if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
            action_required = 1;
    }
1235

1236 1237
    return action_required;
}
1238

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

1268 1269 1270 1271 1272 1273 1274
static void get_arg(char *buf, int buf_size, const char **pp)
{
    const char *p;
    char *q;
    int quote;

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

1300 1301 1302 1303 1304 1305 1306 1307
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);
1308
    if (av_strcasecmp(arg, "allow") == 0)
1309
        acl.action = IP_ALLOW;
1310
    else if (av_strcasecmp(arg, "deny") == 0)
1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388
        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;
1389
        while (av_isspace(*p))
1390 1391 1392 1393 1394
            p++;
        if (*p == '\0' || *p == '#')
            continue;
        get_arg(cmd, sizeof(cmd), &p);

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

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

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

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

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

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

    p = c->buffer;
1505
    get_word(cmd, sizeof(cmd), &p);
1506
    av_strlcpy(c->method, cmd, sizeof(c->method));
1507

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

1515
    get_word(url, sizeof(url), &p);
1516
    av_strlcpy(c->url, url, sizeof(c->url));
1517

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

1522
    av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
1523 1524

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

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

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

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

        p++;
    }

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

1570 1571
    // "redirect" / request to index.html
    if (!strlen(filename))
1572
        av_strlcpy(filename, "index.html", sizeof(filename) - 1);
1573

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

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

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

1619 1620 1621
    if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE)
        current_bandwidth += stream->bandwidth;

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

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

1649
    if (redir_type != REDIR_NONE) {
1650
        const char *hostinfo = 0;
1651

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

            p++;
        }

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

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

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

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

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

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

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

1775
    stream->conns_served++;
1776

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

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

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

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

1827 1828
                if (wmpc && modify_current_stream(wmpc, ratebuf))
                    wmpc->switch_pending = 1;
1829
            }
1830

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

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

Fabrice Bellard's avatar
Fabrice Bellard committed
1849
    if (c->stream->stream_type == STREAM_TYPE_STATUS)
1850
        goto send_status;
Fabrice Bellard's avatar
Fabrice Bellard committed
1851 1852 1853

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

    /* prepare http header */
1859 1860
    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
1861 1862
    mime_type = c->stream->fmt->mime_type;
    if (!mime_type)
1863
        mime_type = "application/x-octet-stream";
1864
    av_strlcatf(c->buffer, c->buffer_size, "Pragma: no-cache\r\n");
Fabrice Bellard's avatar
Fabrice Bellard committed
1865 1866

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

1870
        c->wmp_client_id = av_lfg_get(&random_state);
1871

1872
        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
1873
    }
1874 1875 1876
    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);
1877

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

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

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

1916
    avio_printf(pb, "%"PRId64"%c", count, *s);
1917 1918
}

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

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

1935 1936 1937 1938
    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");
1939

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

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

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

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

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

2045
#if defined(linux)
2046 2047 2048 2049 2050
                {
                    FILE *pid_stat;
                    char ps_cmd[64];

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

2055 2056 2057 2058
                    pid_stat = popen(ps_cmd, "r");
                    if (pid_stat) {
                        char cpuperc[10];
                        char cpuused[64];
2059

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

2070
                avio_printf(pb, "<p>");
2071
            }
2072
            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");
2073 2074 2075

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

                parameters[0] = 0;
2081

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

2100
        }
2101 2102
        stream = stream->next;
    }
2103

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

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

2110
    avio_printf(pb, "Bandwidth in use: %"PRIu64"k / %"PRIu64"k<br>\n",
2111
                 current_bandwidth, max_bandwidth);
2112

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

2289 2290 2291
        c->got_key_frame = 0;

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

2298 2299 2300 2301 2302 2303 2304
        /*
         * 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);

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

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

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

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

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

Fabrice Bellard's avatar
Fabrice Bellard committed
2475 2476 2477 2478 2479 2480 2481
        c->last_packet_sent = 1;
        break;
    }
    return 0;
}

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

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

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

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

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

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

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

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

2610 2611 2612 2613
    /* Don't permit writing to this one */
    if (c->stream->readonly)
        return -1;

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

2622 2623 2624 2625
    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);
2626 2627 2628 2629
        if (ftruncate(c->feed_fd, FFM_PACKET_SIZE) < 0) {
            http_log("Error truncating feed file: %s\n", strerror(errno));
            return -1;
        }
2630
    } else {
2631 2632 2633 2634
        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;
        }
2635 2636
    }

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

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

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

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

2702 2703 2704 2705 2706 2707 2708 2709
    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
2710
    if (c->buffer_ptr >= c->buffer_end) {
2711
        FFStream *feed = c->stream;
Fabrice Bellard's avatar
Fabrice Bellard committed
2712 2713 2714 2715 2716
        /* 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
2717 2718 2719 2720
            if (write(c->feed_fd, c->buffer, FFM_PACKET_SIZE) < 0) {
                http_log("Error writing to feed file: %s\n", strerror(errno));
                goto fail;
            }
2721

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

            /* write index */
2732 2733 2734 2735
            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
2736 2737 2738

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

2750 2751 2752
            if (!s)
                goto fail;

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

2758 2759
            pb = avio_alloc_context(c->buffer, c->buffer_end - c->buffer,
                                    0, NULL, NULL, NULL, NULL);
2760
            pb->seekable = 0;
2761

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

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

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

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

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

2802 2803 2804 2805 2806 2807 2808
/********************************************************************/
/* RTSP handling */

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

    switch(error_number) {
2813 2814 2815 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
    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;
2846 2847 2848 2849
    default:
        str = "Unknown Error";
        break;
    }
2850

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

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

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

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

2877 2878
    c->buffer_ptr[0] = '\0';
    p = c->buffer;
2879

2880 2881 2882 2883
    get_word(cmd, sizeof(cmd), &p);
    get_word(url, sizeof(url), &p);
    get_word(protocol, sizeof(protocol), &p);

2884 2885 2886
    av_strlcpy(c->method, cmd, sizeof(c->method));
    av_strlcpy(c->url, url, sizeof(c->url));
    av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
2887

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

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

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

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

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

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

2980 2981 2982 2983 2984 2985 2986
    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;

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

 sdp_done:
    av_free(avc->streams);
2996
    av_dict_free(&avc->metadata);
2997
    av_free(avc);
2998
    av_free(avs);
2999 3000

    return strlen(*pbuffer);
3001 3002
}

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

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

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

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

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

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

3073
static RTSPTransportField *find_transport(RTSPMessageHeader *h, enum RTSPLowerTransport lower_transport)
3074 3075 3076 3077 3078 3079
{
    RTSPTransportField *th;
    int i;

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

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

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

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

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

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

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

3168 3169 3170 3171 3172 3173
    /* 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;
    }
3174

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

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

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

3225

3226
    avio_printf(c->pb, "\r\n");
3227 3228 3229 3230 3231
}


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

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

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

3266
static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPMessageHeader *h)
3267 3268 3269 3270 3271 3272 3273 3274
{
    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;
    }
3275

3276 3277 3278 3279 3280 3281 3282 3283
    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;
3284

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

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

3302 3303 3304 3305 3306
    if (rtp_c->state != HTTPSTATE_SEND_DATA &&
        rtp_c->state != HTTPSTATE_WAIT_FEED) {
        rtsp_reply_error(c, RTSP_STATUS_STATE);
        return;
    }
3307

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

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

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

    /* abort the session */
    close_connection(rtp_c);
3335 3336 3337 3338 3339 3340
}


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

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

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

3353 3354 3355 3356
    /* add a new connection */
    c = av_mallocz(sizeof(HTTPContext));
    if (!c)
        goto fail;
3357

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

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

3390 3391
    current_bandwidth += stream->bandwidth;

3392 3393 3394
    c->next = first_http_ctx;
    first_http_ctx = c;
    return c;
3395

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

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

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

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

3442 3443 3444 3445
    /* build destination RTP address */
    ipaddr = inet_ntoa(dest_addr->sin_addr);

    switch(c->rtp_protocol) {
3446 3447
    case RTSP_LOWER_TRANSPORT_UDP:
    case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
3448
        /* RTP/UDP case */
3449

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

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

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

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

3497 3498 3499 3500 3501 3502 3503
    c->rtp_ctx[stream_index] = ctx;
    return 0;
}

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

3504
static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec, int copy)
3505 3506 3507
{
    AVStream *fst;

3508 3509 3510
    if(stream->nb_streams >= FF_ARRAY_ELEMS(stream->streams))
        return NULL;

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

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

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

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

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

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

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

3597 3598
    infile->flags |= AVFMT_FLAG_NOFILLIN | AVFMT_FLAG_NOPARSE;

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

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

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

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

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

3678
                for(i=0;i<infile->nb_streams;i++)
3679
                    add_av_stream1(stream, infile->streams[i]->codec, 1);
3680

3681
                avformat_close_input(&infile);
3682 3683 3684 3685 3686
            }
        }
    }
}

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

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

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

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

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

3736 3737
                            ccf = sf->codec;
                            ccs = ss->codec;
3738 3739
#define CHECK_CODEC(x)  (ccf->x != ccs->x)

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

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

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

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

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

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

        close(fd);
    }
}

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

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

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

3860
    if(stream->nb_streams >= FF_ARRAY_ELEMS(stream->streams))
3861
        return;
3862

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

3897
        if (!av->nsse_weight)
3898 3899 3900
            av->nsse_weight = 8;

        av->frame_skip_cmp = FF_CMP_DCTMAX;
3901
        if (!av->me_method)
Martin Storsjö's avatar
Martin Storsjö committed
3902
            av->me_method = ME_EPZS;
3903 3904
        av->rc_buffer_aggressivity = 1.0;

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

3916 3917 3918 3919 3920
        if (av->rc_max_rate && !av->rc_buffer_size) {
            av->rc_buffer_size = av->rc_max_rate;
        }


Fabrice Bellard's avatar
Fabrice Bellard committed
3921
        break;
3922
    default:
3923
        abort();
Fabrice Bellard's avatar
Fabrice Bellard committed
3924 3925 3926 3927 3928
    }

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

3934
static enum AVCodecID opt_audio_codec(const char *arg)
3935
{
3936
    AVCodec *p= avcodec_find_encoder_by_name(arg);
3937

3938
    if (p == NULL || p->type != AVMEDIA_TYPE_AUDIO)
3939
        return AV_CODEC_ID_NONE;
3940 3941 3942 3943

    return p->id;
}

3944
static enum AVCodecID opt_video_codec(const char *arg)
3945
{
3946
    AVCodec *p= avcodec_find_encoder_by_name(arg);
3947

3948
    if (p == NULL || p->type != AVMEDIA_TYPE_VIDEO)
3949
        return AV_CODEC_ID_NONE;
3950 3951 3952 3953

    return p->id;
}

3954
static int ffserver_opt_default(const char *opt, const char *arg,
3955 3956
                       AVCodecContext *avctx, int type)
{
3957
    int ret = 0;
3958
    const AVOption *o = av_opt_find(avctx, opt, NULL, type, 0);
3959
    if(o)
3960
        ret = av_opt_set(avctx, opt, arg, 0);
3961
    return ret;
3962 3963
}

3964 3965
static int ffserver_opt_preset(const char *arg,
                       AVCodecContext *avctx, int type,
3966
                       enum AVCodecID *audio_id, enum AVCodecID *video_id)
3967 3968 3969
{
    FILE *f=NULL;
    char filename[1000], tmp[1000], tmp2[1000], line[1000];
3970 3971
    int ret = 0;
    AVCodec *codec = avcodec_find_encoder(avctx->codec_id);
3972

3973 3974
    if (!(f = get_preset_file(filename, sizeof(filename), arg, 0,
                              codec ? codec->name : NULL))) {
3975 3976 3977 3978 3979 3980 3981 3982 3983 3984 3985 3986 3987 3988 3989 3990 3991 3992 3993 3994 3995 3996 3997 3998 3999 4000 4001 4002 4003 4004 4005 4006
        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;
}

4007 4008 4009
static AVOutputFormat *ffserver_guess_format(const char *short_name, const char *filename,
                                             const char *mime_type)
{
4010
    AVOutputFormat *fmt = av_guess_format(short_name, filename, mime_type);
4011 4012 4013 4014 4015 4016

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

        snprintf(stream_format_name, sizeof(stream_format_name), "%s_stream", fmt->name);
4017
        stream_fmt = av_guess_format(stream_format_name, NULL, NULL);
4018 4019 4020 4021 4022 4023 4024 4025

        if (stream_fmt)
            fmt = stream_fmt;
    }

    return fmt;
}

4026 4027 4028 4029 4030 4031 4032 4033 4034 4035 4036
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)++;
}

4037
static int parse_ffconfig(const char *filename)
Fabrice Bellard's avatar
Fabrice Bellard committed
4038 4039 4040 4041 4042 4043 4044
{
    FILE *f;
    char line[1024];
    char cmd[64];
    char arg[1024];
    const char *p;
    int val, errors, line_num;
4045
    FFStream **last_stream, *stream, *redirect;
4046
    FFStream **last_feed, *feed, *s;
Fabrice Bellard's avatar
Fabrice Bellard committed
4047
    AVCodecContext audio_enc, video_enc;
4048
    enum AVCodecID audio_id, video_id;
Fabrice Bellard's avatar
Fabrice Bellard committed
4049 4050 4051 4052 4053 4054

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

Fabrice Bellard's avatar
Fabrice Bellard committed
4056 4057 4058 4059 4060 4061 4062 4063
    errors = 0;
    line_num = 0;
    first_stream = NULL;
    last_stream = &first_stream;
    first_feed = NULL;
    last_feed = &first_feed;
    stream = NULL;
    feed = NULL;
4064
    redirect = NULL;
4065 4066
    audio_id = AV_CODEC_ID_NONE;
    video_id = AV_CODEC_ID_NONE;
4067 4068

#define ERROR(...) report_config_error(filename, line_num, &errors, __VA_ARGS__)
Fabrice Bellard's avatar
Fabrice Bellard committed
4069 4070 4071 4072 4073
    for(;;) {
        if (fgets(line, sizeof(line), f) == NULL)
            break;
        line_num++;
        p = line;
4074
        while (av_isspace(*p))
Fabrice Bellard's avatar
Fabrice Bellard committed
4075 4076 4077 4078 4079
            p++;
        if (*p == '\0' || *p == '#')
            continue;

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

4081
        if (!av_strcasecmp(cmd, "Port")) {
Fabrice Bellard's avatar
Fabrice Bellard committed
4082
            get_arg(arg, sizeof(arg), &p);
4083 4084
            val = atoi(arg);
            if (val < 1 || val > 65536) {
4085
                ERROR("Invalid_port: %s\n", arg);
4086 4087
            }
            my_http_addr.sin_port = htons(val);
4088
        } else if (!av_strcasecmp(cmd, "BindAddress")) {
Fabrice Bellard's avatar
Fabrice Bellard committed
4089
            get_arg(arg, sizeof(arg), &p);
4090
            if (resolve_host(&my_http_addr.sin_addr, arg) != 0) {
4091
                ERROR("%s:%d: Invalid host/IP address: %s\n", arg);
4092
            }
4093 4094
        } else if (!av_strcasecmp(cmd, "NoDaemon")) {
            // do nothing here, its the default now
4095
        } else if (!av_strcasecmp(cmd, "RTSPPort")) {
4096
            get_arg(arg, sizeof(arg), &p);
4097 4098
            val = atoi(arg);
            if (val < 1 || val > 65536) {
4099
                ERROR("%s:%d: Invalid port: %s\n", arg);
4100 4101
            }
            my_rtsp_addr.sin_port = htons(atoi(arg));
4102
        } else if (!av_strcasecmp(cmd, "RTSPBindAddress")) {
4103
            get_arg(arg, sizeof(arg), &p);
4104
            if (resolve_host(&my_rtsp_addr.sin_addr, arg) != 0) {
4105
                ERROR("Invalid host/IP address: %s\n", arg);
Fabrice Bellard's avatar
Fabrice Bellard committed
4106
            }
4107
        } else if (!av_strcasecmp(cmd, "MaxHTTPConnections")) {
4108 4109 4110
            get_arg(arg, sizeof(arg), &p);
            val = atoi(arg);
            if (val < 1 || val > 65536) {
4111
                ERROR("Invalid MaxHTTPConnections: %s\n", arg);
4112 4113
            }
            nb_max_http_connections = val;
4114
        } else if (!av_strcasecmp(cmd, "MaxClients")) {
Fabrice Bellard's avatar
Fabrice Bellard committed
4115 4116
            get_arg(arg, sizeof(arg), &p);
            val = atoi(arg);
4117
            if (val < 1 || val > nb_max_http_connections) {
4118
                ERROR("Invalid MaxClients: %s\n", arg);
Fabrice Bellard's avatar
Fabrice Bellard committed
4119 4120 4121
            } else {
                nb_max_connections = val;
            }
4122
        } else if (!av_strcasecmp(cmd, "MaxBandwidth")) {
4123
            int64_t llval;
4124
            get_arg(arg, sizeof(arg), &p);
4125
            llval = strtoll(arg, NULL, 10);
4126
            if (llval < 10 || llval > 10000000) {
4127
                ERROR("Invalid MaxBandwidth: %s\n", arg);
4128
            } else
4129
                max_bandwidth = llval;
4130
        } else if (!av_strcasecmp(cmd, "CustomLog")) {
4131 4132
            if (!ffserver_debug)
                get_arg(logfilename, sizeof(logfilename), &p);
4133
        } else if (!av_strcasecmp(cmd, "<Feed")) {
Fabrice Bellard's avatar
Fabrice Bellard committed
4134 4135 4136 4137
            /*********************************************/
            /* Feed related options */
            char *q;
            if (stream || feed) {
4138
                ERROR("Already in a tag\n");
Fabrice Bellard's avatar
Fabrice Bellard committed
4139 4140 4141 4142 4143 4144
            } else {
                feed = av_mallocz(sizeof(FFStream));
                get_arg(feed->filename, sizeof(feed->filename), &p);
                q = strrchr(feed->filename, '>');
                if (*q)
                    *q = '\0';
4145 4146 4147

                for (s = first_feed; s; s = s->next) {
                    if (!strcmp(feed->filename, s->filename)) {
4148
                        ERROR("Feed '%s' already registered\n", s->filename);
4149 4150 4151
                    }
                }

4152
                feed->fmt = av_guess_format("ffm", NULL, NULL);
Fabrice Bellard's avatar
Fabrice Bellard committed
4153 4154 4155 4156 4157 4158
                /* 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 :-) */
4159 4160 4161 4162 4163 4164 4165

                /* 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
4166
            }
4167
        } else if (!av_strcasecmp(cmd, "Launch")) {
4168 4169 4170
            if (feed) {
                int i;

4171
                feed->child_argv = av_mallocz(64 * sizeof(char *));
4172

4173
                for (i = 0; i < 62; i++) {
Alex Beregszaszi's avatar
Alex Beregszaszi committed
4174 4175
                    get_arg(arg, sizeof(arg), &p);
                    if (!arg[0])
4176 4177
                        break;

Alex Beregszaszi's avatar
Alex Beregszaszi committed
4178
                    feed->child_argv[i] = av_strdup(arg);
4179 4180
                }

4181
                feed->child_argv[i] = av_asprintf("http://%s:%d/%s",
4182 4183 4184
                        (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);
4185
            }
4186
        } else if (!av_strcasecmp(cmd, "ReadOnlyFile")) {
4187 4188 4189 4190 4191 4192
            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);
            }
4193
        } else if (!av_strcasecmp(cmd, "File")) {
Fabrice Bellard's avatar
Fabrice Bellard committed
4194 4195
            if (feed) {
                get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
4196
            } else if (stream)
Fabrice Bellard's avatar
Fabrice Bellard committed
4197
                get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4198
        } else if (!av_strcasecmp(cmd, "Truncate")) {
4199 4200 4201 4202
            if (feed) {
                get_arg(arg, sizeof(arg), &p);
                feed->truncate = strtod(arg, NULL);
            }
4203
        } else if (!av_strcasecmp(cmd, "FileMaxSize")) {
Fabrice Bellard's avatar
Fabrice Bellard committed
4204
            if (feed) {
4205
                char *p1;
Fabrice Bellard's avatar
Fabrice Bellard committed
4206 4207 4208 4209
                double fsize;

                get_arg(arg, sizeof(arg), &p);
                p1 = arg;
4210
                fsize = strtod(p1, &p1);
4211
                switch(av_toupper(*p1)) {
Fabrice Bellard's avatar
Fabrice Bellard committed
4212 4213 4214 4215 4216 4217 4218 4219 4220 4221
                case 'K':
                    fsize *= 1024;
                    break;
                case 'M':
                    fsize *= 1024 * 1024;
                    break;
                case 'G':
                    fsize *= 1024 * 1024 * 1024;
                    break;
                }
4222
                feed->feed_max_size = (int64_t)fsize;
4223
                if (feed->feed_max_size < FFM_PACKET_SIZE*4) {
4224
                    ERROR("Feed max file size is too small, must be at least %d\n", FFM_PACKET_SIZE*4);
4225
                }
Fabrice Bellard's avatar
Fabrice Bellard committed
4226
            }
4227
        } else if (!av_strcasecmp(cmd, "</Feed>")) {
Fabrice Bellard's avatar
Fabrice Bellard committed
4228
            if (!feed) {
4229
                ERROR("No corresponding <Feed> for </Feed>\n");
Fabrice Bellard's avatar
Fabrice Bellard committed
4230 4231
            }
            feed = NULL;
4232
        } else if (!av_strcasecmp(cmd, "<Stream")) {
Fabrice Bellard's avatar
Fabrice Bellard committed
4233 4234 4235 4236
            /*********************************************/
            /* Stream related options */
            char *q;
            if (stream || feed) {
4237
                ERROR("Already in a tag\n");
Fabrice Bellard's avatar
Fabrice Bellard committed
4238
            } else {
4239
                FFStream *s;
Fabrice Bellard's avatar
Fabrice Bellard committed
4240 4241 4242
                stream = av_mallocz(sizeof(FFStream));
                get_arg(stream->filename, sizeof(stream->filename), &p);
                q = strrchr(stream->filename, '>');
4243
                if (q)
Fabrice Bellard's avatar
Fabrice Bellard committed
4244
                    *q = '\0';
4245 4246 4247

                for (s = first_stream; s; s = s->next) {
                    if (!strcmp(stream->filename, s->filename)) {
4248
                        ERROR("Stream '%s' already registered\n", s->filename);
4249 4250 4251
                    }
                }

4252
                stream->fmt = ffserver_guess_format(NULL, stream->filename, NULL);
4253 4254
                avcodec_get_context_defaults3(&video_enc, NULL);
                avcodec_get_context_defaults3(&audio_enc, NULL);
4255

4256 4257
                audio_id = AV_CODEC_ID_NONE;
                video_id = AV_CODEC_ID_NONE;
Fabrice Bellard's avatar
Fabrice Bellard committed
4258 4259 4260 4261
                if (stream->fmt) {
                    audio_id = stream->fmt->audio_codec;
                    video_id = stream->fmt->video_codec;
                }
4262 4263 4264

                *last_stream = stream;
                last_stream = &stream->next;
Fabrice Bellard's avatar
Fabrice Bellard committed
4265
            }
4266
        } else if (!av_strcasecmp(cmd, "Feed")) {
Fabrice Bellard's avatar
Fabrice Bellard committed
4267 4268 4269
            get_arg(arg, sizeof(arg), &p);
            if (stream) {
                FFStream *sfeed;
4270

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

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

4624 4625 4626 4627 4628 4629 4630 4631 4632 4633 4634 4635 4636 4637 4638
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);

4639
                if (uptime < 30)
4640 4641 4642 4643 4644 4645 4646 4647 4648
                    /* Turn off any more restarts */
                    feed->child_argv = 0;
            }
        }
    }

    need_to_start_children = 1;
}

4649
static void opt_debug(void)
4650 4651
{
    ffserver_debug = 1;
4652
    logfilename[0] = '-';
4653 4654
}

4655
void show_help_default(const char *opt, const char *arg)
4656
{
4657
    printf("usage: ffserver [options]\n"
4658 4659
           "Hyper fast multi format Audio/Video streaming server\n");
    printf("\n");
4660
    show_help_options(options, "Main options:", 0, 0, 0);
4661 4662 4663
}

static const OptionDef options[] = {
4664
#include "cmdutils_common_opts.h"
4665 4666 4667 4668 4669 4670
    { "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
4671 4672
int main(int argc, char **argv)
{
4673
    struct sigaction sigact = { { 0 } };
Fabrice Bellard's avatar
Fabrice Bellard committed
4674

4675
    parse_loglevel(argc, argv, options);
4676
    av_register_all();
4677
    avformat_network_init();
Fabrice Bellard's avatar
Fabrice Bellard committed
4678

4679
    show_banner(argc, argv, options);
4680

4681
    my_program_name = argv[0];
4682

4683
    parse_options(NULL, argc, argv, options, NULL);
Fabrice Bellard's avatar
Fabrice Bellard committed
4684

4685 4686 4687
    if (!config_filename)
        config_filename = av_strdup("/etc/ffserver.conf");

4688
    unsetenv("http_proxy");             /* Kill the http_proxy */
4689

4690
    av_lfg_init(&random_state, av_get_random_seed());
4691

4692 4693 4694 4695
    sigact.sa_handler = handle_child_exit;
    sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
    sigaction(SIGCHLD, &sigact, 0);

Fabrice Bellard's avatar
Fabrice Bellard committed
4696 4697 4698 4699
    if (parse_ffconfig(config_filename) < 0) {
        fprintf(stderr, "Incorrect config file - exiting.\n");
        exit(1);
    }
4700
    av_freep(&config_filename);
Fabrice Bellard's avatar
Fabrice Bellard committed
4701

4702 4703 4704
    /* open log file if needed */
    if (logfilename[0] != '\0') {
        if (!strcmp(logfilename, "-"))
4705
            logfile = stdout;
4706 4707 4708 4709 4710
        else
            logfile = fopen(logfilename, "a");
        av_log_set_callback(http_av_log);
    }

4711 4712
    build_file_streams();

Fabrice Bellard's avatar
Fabrice Bellard committed
4713 4714
    build_feed_streams();

4715 4716
    compute_bandwidth();

Fabrice Bellard's avatar
Fabrice Bellard committed
4717 4718 4719
    /* signal init */
    signal(SIGPIPE, SIG_IGN);

4720
    if (http_server() < 0) {
4721
        http_log("Could not start server\n");
Fabrice Bellard's avatar
Fabrice Bellard committed
4722 4723 4724 4725 4726
        exit(1);
    }

    return 0;
}