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

22 23
#define _XOPEN_SOURCE 600

24
#include "config.h"
25
#if !HAVE_CLOSESOCKET
26 27 28
#define closesocket close
#endif
#include <string.h>
29
#include <strings.h>
30
#include <stdlib.h>
31
/* avformat.h defines LIBAVFORMAT_BUILD, include it before all the other libav* headers which use it */
32 33 34
#include "libavformat/avformat.h"
#include "libavformat/network.h"
#include "libavformat/os_support.h"
35
#include "libavformat/rtpdec.h"
36
#include "libavformat/rtsp.h"
37
#include "libavutil/avstring.h"
38 39
#include "libavutil/lfg.h"
#include "libavutil/random_seed.h"
40
#include "libavutil/intreadwrite.h"
41
#include "libavcodec/opt.h"
Fabrice Bellard's avatar
Fabrice Bellard committed
42 43 44 45
#include <stdarg.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
46
#if HAVE_POLL_H
47
#include <poll.h>
48
#endif
Fabrice Bellard's avatar
Fabrice Bellard committed
49 50
#include <errno.h>
#include <sys/time.h>
51
#undef time //needed because HAVE_AV_CONFIG_H is defined on top
Fabrice Bellard's avatar
Fabrice Bellard committed
52
#include <time.h>
53
#include <sys/wait.h>
Fabrice Bellard's avatar
Fabrice Bellard committed
54
#include <signal.h>
55
#if HAVE_DLFCN_H
56
#include <dlfcn.h>
57
#endif
58

59
#include "cmdutils.h"
Fabrice Bellard's avatar
Fabrice Bellard committed
60

61 62
#undef exit

63
const char program_name[] = "FFserver";
64
const int program_birth_year = 2000;
65

66 67
static const OptionDef options[];

Fabrice Bellard's avatar
Fabrice Bellard committed
68 69 70 71
enum HTTPState {
    HTTPSTATE_WAIT_REQUEST,
    HTTPSTATE_SEND_HEADER,
    HTTPSTATE_SEND_DATA_HEADER,
72
    HTTPSTATE_SEND_DATA,          /* sending TCP or UDP data */
Fabrice Bellard's avatar
Fabrice Bellard committed
73
    HTTPSTATE_SEND_DATA_TRAILER,
74
    HTTPSTATE_RECEIVE_DATA,
75 76 77 78 79
    HTTPSTATE_WAIT_FEED,          /* wait for data from the feed */
    HTTPSTATE_READY,

    RTSPSTATE_WAIT_REQUEST,
    RTSPSTATE_SEND_REPLY,
80
    RTSPSTATE_SEND_PACKET,
Fabrice Bellard's avatar
Fabrice Bellard committed
81 82
};

Baptiste Coudurier's avatar
Baptiste Coudurier committed
83
static const char *http_state[] = {
84 85 86
    "HTTP_WAIT_REQUEST",
    "HTTP_SEND_HEADER",

Fabrice Bellard's avatar
Fabrice Bellard committed
87 88 89 90 91
    "SEND_DATA_HEADER",
    "SEND_DATA",
    "SEND_DATA_TRAILER",
    "RECEIVE_DATA",
    "WAIT_FEED",
92 93 94 95
    "READY",

    "RTSP_WAIT_REQUEST",
    "RTSP_SEND_REPLY",
96
    "RTSP_SEND_PACKET",
Fabrice Bellard's avatar
Fabrice Bellard committed
97 98
};

99
#define IOBUFFER_INIT_SIZE 8192
Fabrice Bellard's avatar
Fabrice Bellard committed
100 101

/* timeouts are in ms */
102 103 104
#define HTTP_REQUEST_TIMEOUT (15 * 1000)
#define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)

Fabrice Bellard's avatar
Fabrice Bellard committed
105 106
#define SYNC_TIMEOUT (10 * 1000)

107 108 109 110 111
typedef struct RTSPActionServerSetup {
    uint32_t ipaddr;
    char transport_option[512];
} RTSPActionServerSetup;

112
typedef struct {
113
    int64_t count1, count2;
114
    int64_t time1, time2;
115 116
} DataRateData;

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

162
    /* RTSP state specific */
163
    uint8_t *pb_buffer; /* XXX: use that in all the code */
164 165
    ByteIOContext *pb;
    int seq; /* RTSP sequence number */
166

167
    /* RTP state specific */
168
    enum RTSPLowerTransport rtp_protocol;
169 170
    char session_id[32]; /* session id */
    AVFormatContext *rtp_ctx[MAX_STREAMS];
171

172 173 174 175 176 177
    /* 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
178 179 180 181 182 183
} HTTPContext;

/* each generated stream is described here */
enum StreamType {
    STREAM_TYPE_LIVE,
    STREAM_TYPE_STATUS,
184
    STREAM_TYPE_REDIRECT,
Fabrice Bellard's avatar
Fabrice Bellard committed
185 186
};

187 188 189 190 191 192 193 194
enum IPAddressAction {
    IP_ALLOW = 1,
    IP_DENY,
};

typedef struct IPAddressACL {
    struct IPAddressACL *next;
    enum IPAddressAction action;
195
    /* These are in host order */
196 197 198 199
    struct in_addr first;
    struct in_addr last;
} IPAddressACL;

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

Fabrice Bellard's avatar
Fabrice Bellard committed
236
    /* feed specific */
237
    int feed_opened;     /* true if someone is writing to the feed */
Fabrice Bellard's avatar
Fabrice Bellard committed
238
    int is_feed;         /* true if it is a feed */
239
    int readonly;        /* True if writing is prohibited to the file */
240
    int truncate;        /* True if feeder connection truncate the feed file */
241
    int conns_served;
242
    int64_t bytes_served;
243
    int64_t feed_max_size;      /* maximum storage size, zero means unlimited */
Diego Biurrun's avatar
Diego Biurrun committed
244
    int64_t feed_write_index;   /* current write position in feed (it wraps around) */
245
    int64_t feed_size;          /* current size of feed */
Fabrice Bellard's avatar
Fabrice Bellard committed
246 247 248 249 250
    struct FFStream *next_feed;
} FFStream;

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

254 255
static struct sockaddr_in my_http_addr;
static struct sockaddr_in my_rtsp_addr;
256

257 258 259 260
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
261

262 263 264 265 266
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
267
static int http_parse_request(HTTPContext *c);
268
static int http_send_data(HTTPContext *c);
269
static void compute_status(HTTPContext *c);
Fabrice Bellard's avatar
Fabrice Bellard committed
270 271 272
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);
273 274 275 276

/* RTSP handling */
static int rtsp_parse_request(HTTPContext *c);
static void rtsp_cmd_describe(HTTPContext *c, const char *url);
277
static void rtsp_cmd_options(HTTPContext *c, const char *url);
278 279 280 281
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);
282

283
/* SDP handling */
284
static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
285 286
                                   struct in_addr my_ip);

287
/* RTP handling */
288
static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
289
                                       FFStream *stream, const char *session_id,
290
                                       enum RTSPLowerTransport rtp_protocol);
291
static int rtp_new_av_stream(HTTPContext *c,
292 293
                             int stream_index, struct sockaddr_in *dest_addr,
                             HTTPContext *rtsp_c);
Fabrice Bellard's avatar
Fabrice Bellard committed
294

295
static const char *my_program_name;
296
static const char *my_program_dir;
297

298
static const char *config_filename;
299
static int ffserver_debug;
300
static int ffserver_daemon;
301
static int no_launch;
302
static int need_to_start_children;
303

304 305
/* maximum number of simultaneous HTTP connections */
static unsigned int nb_max_http_connections = 2000;
306 307
static unsigned int nb_max_connections = 5;
static unsigned int nb_connections;
Fabrice Bellard's avatar
Fabrice Bellard committed
308

309
static uint64_t max_bandwidth = 1000;
310
static uint64_t current_bandwidth;
311

312
static int64_t cur_time;           // Making this global saves on passing it around everywhere
313

314
static AVLFG random_state;
315

Fabrice Bellard's avatar
Fabrice Bellard committed
316 317
static FILE *logfile = NULL;

Baptiste Coudurier's avatar
Baptiste Coudurier committed
318 319 320 321 322 323 324 325 326 327 328 329 330 331
static char *ctime1(char *buf2)
{
    time_t ti;
    char *p;

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

332
static void http_vlog(const char *fmt, va_list vargs)
Fabrice Bellard's avatar
Fabrice Bellard committed
333
{
334
    static int print_prefix = 1;
335
    if (logfile) {
336
        if (print_prefix) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
337 338 339
            char buf[32];
            ctime1(buf);
            fprintf(logfile, "%s ", buf);
340 341
        }
        print_prefix = strstr(fmt, "\n") != NULL;
342
        vfprintf(logfile, fmt, vargs);
343 344
        fflush(logfile);
    }
345 346 347 348 349 350 351 352 353 354 355 356 357 358
}

void __attribute__ ((format (printf, 1, 2))) http_log(const char *fmt, ...)
{
    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;
359
    if (level > av_log_get_level())
360 361
        return;
    if (print_prefix && avc)
362
        http_log("[%s @ %p]", avc->item_name(ptr), ptr);
363 364
    print_prefix = strstr(fmt, "\n") != NULL;
    http_vlog(fmt, vargs);
Fabrice Bellard's avatar
Fabrice Bellard committed
365 366
}

367 368
static void log_connection(HTTPContext *c)
{
369
    if (c->suppress_log)
370 371
        return;

372 373
    http_log("%s - - [%s] \"%s %s\" %d %"PRId64"\n",
             inet_ntoa(c->from_addr.sin_addr), c->method, c->url,
374
             c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
375 376
}

377
static void update_datarate(DataRateData *drd, int64_t count)
378 379 380 381
{
    if (!drd->time1 && !drd->count1) {
        drd->time1 = drd->time2 = cur_time;
        drd->count1 = drd->count2 = count;
382
    } else if (cur_time - drd->time2 > 5000) {
Alex Beregszaszi's avatar
Alex Beregszaszi committed
383 384 385 386
        drd->time1 = drd->time2;
        drd->count1 = drd->count2;
        drd->time2 = cur_time;
        drd->count2 = count;
387 388 389 390
    }
}

/* In bytes per second */
391
static int compute_datarate(DataRateData *drd, int64_t count)
392 393 394
{
    if (cur_time == drd->time1)
        return 0;
395

396 397 398
    return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
}

399

400 401
static void start_children(FFStream *feed)
{
402 403 404
    if (no_launch)
        return;

405
    for (; feed; feed = feed->next) {
406 407 408
        if (feed->child_argv && !feed->pid) {
            feed->pid_start = time(0);

409 410 411
            feed->pid = fork();

            if (feed->pid < 0) {
412
                http_log("Unable to create children\n");
413 414 415 416 417 418 419 420
                exit(1);
            }
            if (!feed->pid) {
                /* In child */
                char pathname[1024];
                char *slash;
                int i;

421 422 423 424 425 426 427 428 429
                av_strlcpy(pathname, my_program_name, sizeof(pathname));

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

Stefano Sabatini's avatar
Stefano Sabatini committed
430 431 432 433 434
                http_log("Launch commandline: ");
                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");
435

436
                for (i = 3; i < 256; i++)
437
                    close(i);
438

439
                if (!ffserver_debug) {
440
                    i = open("/dev/null", O_RDWR);
441
                    if (i != -1) {
442
                        dup2(i, 0);
443 444
                        dup2(i, 1);
                        dup2(i, 2);
445
                        close(i);
446
                    }
447
                }
448

449 450 451
                /* This is needed to make relative pathnames work */
                chdir(my_program_dir);

452 453
                signal(SIGPIPE, SIG_DFL);

454 455 456 457 458 459
                execvp(pathname, feed->child_argv);

                _exit(1);
            }
        }
    }
460 461
}

462 463
/* open a listening socket */
static int socket_open_listen(struct sockaddr_in *my_addr)
Fabrice Bellard's avatar
Fabrice Bellard committed
464
{
465
    int server_fd, tmp;
Fabrice Bellard's avatar
Fabrice Bellard committed
466 467 468 469 470 471

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

Fabrice Bellard's avatar
Fabrice Bellard committed
473 474 475
    tmp = 1;
    setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));

476
    if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
477 478 479
        char bindmsg[32];
        snprintf(bindmsg, sizeof(bindmsg), "bind(port %d)", ntohs(my_addr->sin_port));
        perror (bindmsg);
480
        closesocket(server_fd);
Fabrice Bellard's avatar
Fabrice Bellard committed
481 482
        return -1;
    }
483

Fabrice Bellard's avatar
Fabrice Bellard committed
484 485
    if (listen (server_fd, 5) < 0) {
        perror ("listen");
486
        closesocket(server_fd);
Fabrice Bellard's avatar
Fabrice Bellard committed
487 488
        return -1;
    }
489
    ff_socket_nonblock(server_fd, 1);
490 491 492 493

    return server_fd;
}

494 495 496 497 498 499 500 501 502 503 504 505 506
/* start all multicast streams */
static void start_multicast(void)
{
    FFStream *stream;
    char session_id[32];
    HTTPContext *rtp_c;
    struct sockaddr_in dest_addr;
    int default_port, stream_index;

    default_port = 6000;
    for(stream = first_stream; stream != NULL; stream = stream->next) {
        if (stream->is_multicast) {
            /* open the RTP connection */
507
            snprintf(session_id, sizeof(session_id), "%08x%08x",
508
                     av_lfg_get(&random_state), av_lfg_get(&random_state));
509 510 511 512 513 514 515 516 517 518 519

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

520
            rtp_c = rtp_new_connection(&dest_addr, stream, session_id,
521
                                       RTSP_LOWER_TRANSPORT_UDP_MULTICAST);
522
            if (!rtp_c)
523
                continue;
524

525
            if (open_input_stream(rtp_c, "") < 0) {
526 527
                http_log("Could not open input stream for stream '%s'\n",
                         stream->filename);
528 529 530 531
                continue;
            }

            /* open each RTP stream */
532
            for(stream_index = 0; stream_index < stream->nb_streams;
533
                stream_index++) {
534
                dest_addr.sin_port = htons(stream->multicast_port +
535
                                           2 * stream_index);
536
                if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, NULL) < 0) {
537 538
                    http_log("Could not open output stream '%s/streamid=%d'\n",
                             stream->filename, stream_index);
539
                    exit(1);
540 541 542 543 544 545 546 547
                }
            }

            /* change state to send data */
            rtp_c->state = HTTPSTATE_SEND_DATA;
        }
    }
}
548 549 550 551

/* main loop of the http server */
static int http_server(void)
{
552 553
    int server_fd = 0, rtsp_server_fd = 0;
    int ret, delay, delay1;
554
    struct pollfd *poll_table, *poll_entry;
555 556
    HTTPContext *c, *c_next;

557
    if(!(poll_table = av_mallocz((nb_max_http_connections + 2)*sizeof(*poll_table)))) {
558 559 560 561
        http_log("Impossible to allocate a poll table handling %d connections.\n", nb_max_http_connections);
        return -1;
    }

562
    if (my_http_addr.sin_port) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
563 564 565
        server_fd = socket_open_listen(&my_http_addr);
        if (server_fd < 0)
            return -1;
566
    }
Fabrice Bellard's avatar
Fabrice Bellard committed
567

568
    if (my_rtsp_addr.sin_port) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
569 570 571
        rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
        if (rtsp_server_fd < 0)
            return -1;
572 573 574 575 576 577
    }

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

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

581 582
    start_children(first_feed);

583 584
    start_multicast();

Fabrice Bellard's avatar
Fabrice Bellard committed
585 586
    for(;;) {
        poll_entry = poll_table;
587
        if (server_fd) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
588 589 590
            poll_entry->fd = server_fd;
            poll_entry->events = POLLIN;
            poll_entry++;
591 592
        }
        if (rtsp_server_fd) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
593 594 595
            poll_entry->fd = rtsp_server_fd;
            poll_entry->events = POLLIN;
            poll_entry++;
596
        }
597

Fabrice Bellard's avatar
Fabrice Bellard committed
598 599
        /* wait for events on each HTTP handle */
        c = first_http_ctx;
600
        delay = 1000;
Fabrice Bellard's avatar
Fabrice Bellard committed
601 602 603 604
        while (c != NULL) {
            int fd;
            fd = c->fd;
            switch(c->state) {
605 606
            case HTTPSTATE_SEND_HEADER:
            case RTSPSTATE_SEND_REPLY:
607
            case RTSPSTATE_SEND_PACKET:
Fabrice Bellard's avatar
Fabrice Bellard committed
608 609
                c->poll_entry = poll_entry;
                poll_entry->fd = fd;
610
                poll_entry->events = POLLOUT;
Fabrice Bellard's avatar
Fabrice Bellard committed
611 612 613 614 615
                poll_entry++;
                break;
            case HTTPSTATE_SEND_DATA_HEADER:
            case HTTPSTATE_SEND_DATA:
            case HTTPSTATE_SEND_DATA_TRAILER:
616 617 618 619 620 621 622
                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 {
623 624 625 626 627 628
                    /* 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;
629
                }
Fabrice Bellard's avatar
Fabrice Bellard committed
630
                break;
631
            case HTTPSTATE_WAIT_REQUEST:
Fabrice Bellard's avatar
Fabrice Bellard committed
632 633
            case HTTPSTATE_RECEIVE_DATA:
            case HTTPSTATE_WAIT_FEED:
634
            case RTSPSTATE_WAIT_REQUEST:
Fabrice Bellard's avatar
Fabrice Bellard committed
635 636 637
                /* need to catch errors */
                c->poll_entry = poll_entry;
                poll_entry->fd = fd;
638
                poll_entry->events = POLLIN;/* Maybe this will work */
Fabrice Bellard's avatar
Fabrice Bellard committed
639 640 641 642 643 644 645 646 647 648 649 650
                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 {
651
            ret = poll(poll_table, poll_entry - poll_table, delay);
652 653
            if (ret < 0 && ff_neterrno() != FF_NETERROR(EAGAIN) &&
                ff_neterrno() != FF_NETERROR(EINTR))
654
                return -1;
655
        } while (ret < 0);
656

657
        cur_time = av_gettime() / 1000;
Fabrice Bellard's avatar
Fabrice Bellard committed
658

659 660 661 662 663
        if (need_to_start_children) {
            need_to_start_children = 0;
            start_children(first_feed);
        }

Fabrice Bellard's avatar
Fabrice Bellard committed
664
        /* now handle the events */
665 666 667
        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
668
                /* close and free the connection */
669
                log_connection(c);
670
                close_connection(c);
Fabrice Bellard's avatar
Fabrice Bellard committed
671 672 673 674
            }
        }

        poll_entry = poll_table;
675
        if (server_fd) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
676 677 678 679
            /* new HTTP connection request ? */
            if (poll_entry->revents & POLLIN)
                new_connection(server_fd, 0);
            poll_entry++;
680 681
        }
        if (rtsp_server_fd) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
682 683 684
            /* new RTSP connection request ? */
            if (poll_entry->revents & POLLIN)
                new_connection(rtsp_server_fd, 1);
685
        }
Fabrice Bellard's avatar
Fabrice Bellard committed
686 687 688
    }
}

689 690
/* 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
691
{
692 693 694 695 696 697 698 699 700 701 702 703
    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;
    }
}

704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719
static void http_send_too_busy_reply(int fd)
{
    char buffer[300];
    int len = snprintf(buffer, sizeof(buffer),
                       "HTTP/1.0 200 Server too busy\r\n"
                       "Content-type: text/html\r\n"
                       "\r\n"
                       "<html><head><title>Too busy</title></head><body>\r\n"
                       "<p>The server is too busy to serve your request at this time.</p>\r\n"
                       "<p>The number of current connections is %d, and this exceeds the limit of %d.</p>\r\n"
                       "</body></html>\r\n",
                       nb_connections, nb_max_connections);
    send(fd, buffer, len, 0);
}


720 721 722 723 724 725 726
static void new_connection(int server_fd, int is_rtsp)
{
    struct sockaddr_in from_addr;
    int fd, len;
    HTTPContext *c = NULL;

    len = sizeof(from_addr);
727
    fd = accept(server_fd, (struct sockaddr *)&from_addr,
728
                &len);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
729 730
    if (fd < 0) {
        http_log("error during accept %s\n", strerror(errno));
731
        return;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
732
    }
733
    ff_socket_nonblock(fd, 1);
734

735 736
    if (nb_connections >= nb_max_connections) {
        http_send_too_busy_reply(fd);
737
        goto fail;
738
    }
739

740 741 742 743
    /* add a new connection */
    c = av_mallocz(sizeof(HTTPContext));
    if (!c)
        goto fail;
744

745 746 747 748 749 750 751
    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;
752 753 754

    c->next = first_http_ctx;
    first_http_ctx = c;
755
    nb_connections++;
756

757 758 759 760 761 762 763 764 765
    start_wait_request(c, is_rtsp);

    return;

 fail:
    if (c) {
        av_free(c->buffer);
        av_free(c);
    }
766
    closesocket(fd);
767 768 769 770 771 772 773 774 775 776 777 778 779 780
}

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;
781
        if (c1 == c)
782
            *cp = c->next;
783
        else
784 785 786
            cp = &c1->next;
    }

787 788 789 790 791 792
    /* 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;
    }

793 794
    /* remove connection associated resources */
    if (c->fd >= 0)
795
        closesocket(c->fd);
796 797 798 799
    if (c->fmt_in) {
        /* close each frame parser */
        for(i=0;i<c->fmt_in->nb_streams;i++) {
            st = c->fmt_in->streams[i];
800
            if (st->codec->codec)
801
                avcodec_close(st->codec);
802 803 804 805 806 807
        }
        av_close_input_file(c->fmt_in);
    }

    /* free RTP output streams if any */
    nb_streams = 0;
808
    if (c->stream)
809
        nb_streams = c->stream->nb_streams;
810

811 812 813 814 815 816 817
    for(i=0;i<nb_streams;i++) {
        ctx = c->rtp_ctx[i];
        if (ctx) {
            av_write_trailer(ctx);
            av_free(ctx);
        }
        h = c->rtp_handles[i];
818
        if (h)
819 820
            url_close(h);
    }
821

822 823
    ctx = &c->fmt_ctx;

824
    if (!c->last_packet_sent && c->state == HTTPSTATE_SEND_DATA_TRAILER) {
825 826 827 828
        if (ctx->oformat) {
            /* prepare header */
            if (url_open_dyn_buf(&ctx->pb) >= 0) {
                av_write_trailer(ctx);
829
                av_freep(&c->pb_buffer);
830
                url_close_dyn_buf(ctx->pb, &c->pb_buffer);
831 832 833 834
            }
        }
    }

835
    for(i=0; i<ctx->nb_streams; i++)
Alex Beregszaszi's avatar
Alex Beregszaszi committed
836
        av_free(ctx->streams[i]);
837

838
    if (c->stream && !c->post && c->stream->stream_type == STREAM_TYPE_LIVE)
839
        current_bandwidth -= c->stream->bandwidth;
840 841 842 843 844 845 846

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

847
    av_freep(&c->pb_buffer);
848
    av_freep(&c->packet_buffer);
849 850 851 852 853 854 855 856
    av_free(c->buffer);
    av_free(c);
    nb_connections--;
}

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

Fabrice Bellard's avatar
Fabrice Bellard committed
858 859
    switch(c->state) {
    case HTTPSTATE_WAIT_REQUEST:
860
    case RTSPSTATE_WAIT_REQUEST:
Fabrice Bellard's avatar
Fabrice Bellard committed
861 862 863 864 865 866 867 868 869 870
        /* 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 */
871
    read_loop:
872
        len = recv(c->fd, c->buffer_ptr, 1, 0);
Fabrice Bellard's avatar
Fabrice Bellard committed
873
        if (len < 0) {
874 875
            if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
                ff_neterrno() != FF_NETERROR(EINTR))
Fabrice Bellard's avatar
Fabrice Bellard committed
876 877 878 879
                return -1;
        } else if (len == 0) {
            return -1;
        } else {
880
            /* search for end of request. */
881
            uint8_t *ptr;
Fabrice Bellard's avatar
Fabrice Bellard committed
882 883 884 885 886
            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 */
887 888 889 890 891 892
                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
893 894 895 896
                    return -1;
            } else if (ptr >= c->buffer_end) {
                /* request too long: cannot do anything */
                return -1;
897
            } else goto read_loop;
Fabrice Bellard's avatar
Fabrice Bellard committed
898 899 900 901 902 903 904
        }
        break;

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

905
        /* no need to write if no events */
Fabrice Bellard's avatar
Fabrice Bellard committed
906 907
        if (!(c->poll_entry->revents & POLLOUT))
            return 0;
908
        len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
Fabrice Bellard's avatar
Fabrice Bellard committed
909
        if (len < 0) {
910 911
            if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
                ff_neterrno() != FF_NETERROR(EINTR)) {
Fabrice Bellard's avatar
Fabrice Bellard committed
912
                /* error : close connection */
913
                av_freep(&c->pb_buffer);
Fabrice Bellard's avatar
Fabrice Bellard committed
914 915 916 917
                return -1;
            }
        } else {
            c->buffer_ptr += len;
918 919
            if (c->stream)
                c->stream->bytes_served += len;
920
            c->data_count += len;
Fabrice Bellard's avatar
Fabrice Bellard committed
921
            if (c->buffer_ptr >= c->buffer_end) {
922
                av_freep(&c->pb_buffer);
Fabrice Bellard's avatar
Fabrice Bellard committed
923
                /* if error, exit */
924
                if (c->http_error)
Fabrice Bellard's avatar
Fabrice Bellard committed
925
                    return -1;
926
                /* all the buffer was sent : synchronize to the incoming stream */
Fabrice Bellard's avatar
Fabrice Bellard committed
927 928 929 930 931 932 933 934 935
                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:
936 937 938 939 940 941
        /* 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;
942

943 944 945 946
            /* no need to read if no events */
            if (!(c->poll_entry->revents & POLLOUT))
                return 0;
        }
947
        if (http_send_data(c) < 0)
Fabrice Bellard's avatar
Fabrice Bellard committed
948
            return -1;
949 950 951
        /* close connection if trailer sent */
        if (c->state == HTTPSTATE_SEND_DATA_TRAILER)
            return -1;
Fabrice Bellard's avatar
Fabrice Bellard committed
952 953 954 955 956 957 958 959 960 961 962 963
        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 */
964
        if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
Fabrice Bellard's avatar
Fabrice Bellard committed
965 966 967 968
            return -1;

        /* nothing to do, we'll be waken up by incoming feed packets */
        break;
969 970 971 972 973 974 975 976 977

    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;
978
        len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
979
        if (len < 0) {
980 981
            if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
                ff_neterrno() != FF_NETERROR(EINTR)) {
982 983 984 985 986 987 988 989 990 991 992 993 994 995
                /* 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;
996 997 998 999 1000 1001 1002 1003
    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;
1004 1005
        len = send(c->fd, c->packet_buffer_ptr,
                    c->packet_buffer_end - c->packet_buffer_ptr, 0);
1006
        if (len < 0) {
1007 1008
            if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
                ff_neterrno() != FF_NETERROR(EINTR)) {
1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021
                /* 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;
1022 1023 1024
    case HTTPSTATE_READY:
        /* nothing to do */
        break;
Fabrice Bellard's avatar
Fabrice Bellard committed
1025 1026 1027 1028 1029 1030
    default:
        return -1;
    }
    return 0;
}

1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047
static int extract_rates(char *rates, int ratelen, const char *request)
{
    const char *p;

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

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

            if (strncasecmp(q, "stream-switch-entry=", 20) == 0) {
                int stream_no;
                int rate_no;

                q += 20;

1048
                memset(rates, 0xff, ratelen);
1049 1050 1051 1052 1053

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

1054
                    if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2)
1055
                        break;
1056

1057
                    stream_no--;
1058
                    if (stream_no < ratelen && stream_no >= 0)
1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077
                        rates[stream_no] = rate_no;

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

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

        p++;
    }

    return 0;
}

1078
static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
1079 1080
{
    int i;
1081 1082 1083 1084
    int best_bitrate = 100000000;
    int best = -1;

    for (i = 0; i < feed->nb_streams; i++) {
1085
        AVCodecContext *feed_codec = feed->streams[i]->codec;
1086 1087 1088 1089

        if (feed_codec->codec_id != codec->codec_id ||
            feed_codec->sample_rate != codec->sample_rate ||
            feed_codec->width != codec->width ||
1090
            feed_codec->height != codec->height)
1091 1092 1093 1094
            continue;

        /* Potential stream */

1095
        /* We want the fastest stream less than bit_rate, or the slowest
1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119
         * 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;
1120

1121 1122 1123 1124
    /* Not much we can do for a feed */
    if (!req->feed)
        return 0;

1125
    for (i = 0; i < req->nb_streams; i++) {
1126
        AVCodecContext *codec = req->streams[i]->codec;
1127 1128 1129

        switch(rates[i]) {
            case 0:
1130
                c->switch_feed_streams[i] = req->feed_streams[i];
1131 1132
                break;
            case 1:
1133
                c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
1134 1135
                break;
            case 2:
1136 1137 1138 1139 1140 1141 1142
                /* 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
1143 1144 1145
                break;
        }

1146 1147 1148
        if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
            action_required = 1;
    }
1149

1150 1151
    return action_required;
}
1152 1153


1154 1155 1156
static void do_switch_stream(HTTPContext *c, int i)
{
    if (c->switch_feed_streams[i] >= 0) {
1157
#ifdef PHILIP
1158 1159
        c->feed_streams[i] = c->switch_feed_streams[i];
#endif
1160

1161
        /* Now update the stream */
1162
    }
1163
    c->switch_feed_streams[i] = -1;
1164
}
1165

1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194
/* XXX: factorize in utils.c ? */
/* XXX: take care with different space meaning */
static void skip_spaces(const char **pp)
{
    const char *p;
    p = *pp;
    while (*p == ' ' || *p == '\t')
        p++;
    *pp = p;
}

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

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

1195 1196 1197 1198 1199
static int validate_acl(FFStream *stream, HTTPContext *c)
{
    enum IPAddressAction last_action = IP_DENY;
    IPAddressACL *acl;
    struct in_addr *src = &c->from_addr.sin_addr;
1200
    unsigned long src_addr = src->s_addr;
1201 1202

    for (acl = stream->acl; acl; acl = acl->next) {
1203
        if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr)
1204 1205 1206 1207 1208 1209 1210 1211
            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;
}

1212 1213 1214 1215 1216 1217 1218 1219 1220 1221
/* 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 */
1222
    av_strlcpy(file1, filename, sizeof(file1));
1223 1224 1225 1226
    p = strrchr(file1, '.');
    if (p)
        *p = '\0';
    for(stream = first_stream; stream != NULL; stream = stream->next) {
1227
        av_strlcpy(file2, stream->filename, sizeof(file2));
1228 1229 1230 1231
        p = strrchr(file2, '.');
        if (p)
            *p = '\0';
        if (!strcmp(file1, file2)) {
1232
            av_strlcpy(filename, stream->filename, max_size);
1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246
            break;
        }
    }
}

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

Fabrice Bellard's avatar
Fabrice Bellard committed
1247 1248 1249 1250
/* parse http request and prepare header */
static int http_parse_request(HTTPContext *c)
{
    char *p;
1251
    enum RedirType redir_type;
Fabrice Bellard's avatar
Fabrice Bellard committed
1252
    char cmd[32];
1253
    char info[1024], filename[1024];
Fabrice Bellard's avatar
Fabrice Bellard committed
1254 1255 1256 1257 1258
    char url[1024], *q;
    char protocol[32];
    char msg[1024];
    const char *mime_type;
    FFStream *stream;
1259
    int i;
1260
    char ratebuf[32];
1261
    char *useragent = 0;
Fabrice Bellard's avatar
Fabrice Bellard committed
1262 1263

    p = c->buffer;
1264
    get_word(cmd, sizeof(cmd), (const char **)&p);
1265
    av_strlcpy(c->method, cmd, sizeof(c->method));
1266

Fabrice Bellard's avatar
Fabrice Bellard committed
1267
    if (!strcmp(cmd, "GET"))
1268
        c->post = 0;
Fabrice Bellard's avatar
Fabrice Bellard committed
1269
    else if (!strcmp(cmd, "POST"))
1270
        c->post = 1;
Fabrice Bellard's avatar
Fabrice Bellard committed
1271 1272 1273
    else
        return -1;

1274
    get_word(url, sizeof(url), (const char **)&p);
1275
    av_strlcpy(c->url, url, sizeof(c->url));
1276

1277
    get_word(protocol, sizeof(protocol), (const char **)&p);
Fabrice Bellard's avatar
Fabrice Bellard committed
1278 1279
    if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
        return -1;
1280

1281
    av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
1282 1283

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

Fabrice Bellard's avatar
Fabrice Bellard committed
1286
    /* find the filename and the optional info string in the request */
1287
    p = strchr(url, '?');
Fabrice Bellard's avatar
Fabrice Bellard committed
1288
    if (p) {
1289
        av_strlcpy(info, p, sizeof(info));
Fabrice Bellard's avatar
Fabrice Bellard committed
1290
        *p = '\0';
1291
    } else
Fabrice Bellard's avatar
Fabrice Bellard committed
1292 1293
        info[0] = '\0';

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

1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309
    for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
        if (strncasecmp(p, "User-Agent:", 11) == 0) {
            useragent = p + 11;
            if (*useragent && *useragent != '\n' && isspace(*useragent))
                useragent++;
            break;
        }
        p = strchr(p, '\n');
        if (!p)
            break;

        p++;
    }

1310 1311 1312
    redir_type = REDIR_NONE;
    if (match_ext(filename, "asx")) {
        redir_type = REDIR_ASX;
1313
        filename[strlen(filename)-1] = 'f';
1314
    } else if (match_ext(filename, "asf") &&
1315 1316
        (!useragent || strncasecmp(useragent, "NSPlayer", 8) != 0)) {
        /* if this isn't WMP or lookalike, return the redirector file */
1317 1318 1319
        redir_type = REDIR_ASF;
    } else if (match_ext(filename, "rpm,ram")) {
        redir_type = REDIR_RAM;
1320
        strcpy(filename + strlen(filename)-2, "m");
1321 1322
    } else if (match_ext(filename, "rtsp")) {
        redir_type = REDIR_RTSP;
1323
        compute_real_filename(filename, sizeof(filename) - 1);
1324 1325
    } else if (match_ext(filename, "sdp")) {
        redir_type = REDIR_SDP;
1326
        compute_real_filename(filename, sizeof(filename) - 1);
1327
    }
1328

1329 1330
    // "redirect" / request to index.html
    if (!strlen(filename))
1331
        av_strlcpy(filename, "index.html", sizeof(filename) - 1);
1332

Fabrice Bellard's avatar
Fabrice Bellard committed
1333 1334
    stream = first_stream;
    while (stream != NULL) {
1335
        if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
Fabrice Bellard's avatar
Fabrice Bellard committed
1336 1337 1338 1339
            break;
        stream = stream->next;
    }
    if (stream == NULL) {
1340
        snprintf(msg, sizeof(msg), "File '%s' not found", url);
1341
        http_log("File '%s' not found\n", url);
Fabrice Bellard's avatar
Fabrice Bellard committed
1342 1343
        goto send_error;
    }
1344

1345 1346 1347 1348 1349 1350 1351
    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;
1352 1353 1354 1355 1356 1357 1358 1359
        q += snprintf(q, c->buffer_size,
                      "HTTP/1.0 301 Moved\r\n"
                      "Location: %s\r\n"
                      "Content-type: text/html\r\n"
                      "\r\n"
                      "<html><head><title>Moved</title></head><body>\r\n"
                      "You should be <a href=\"%s\">redirected</a>.\r\n"
                      "</body></html>\r\n", stream->feed_filename, stream->feed_filename);
1360 1361 1362 1363 1364 1365 1366
        /* prepare output buffer */
        c->buffer_ptr = c->buffer;
        c->buffer_end = q;
        c->state = HTTPSTATE_SEND_HEADER;
        return 0;
    }

1367 1368
    /* If this is WMP, get the rate information */
    if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1369
        if (modify_current_stream(c, ratebuf)) {
1370
            for (i = 0; i < FF_ARRAY_ELEMS(c->feed_streams); i++) {
1371 1372 1373 1374
                if (c->switch_feed_streams[i] >= 0)
                    do_switch_stream(c, i);
            }
        }
1375 1376
    }

1377 1378 1379
    if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE)
        current_bandwidth += stream->bandwidth;

Diego Biurrun's avatar
Diego Biurrun committed
1380
    /* If already streaming this feed, do not let start another feeder. */
1381 1382
    if (stream->feed_opened) {
        snprintf(msg, sizeof(msg), "This feed is already being received.");
1383
        http_log("Feed '%s' already being received\n", stream->feed_filename);
1384 1385 1386
        goto send_error;
    }

1387
    if (c->post == 0 && max_bandwidth < current_bandwidth) {
1388 1389
        c->http_error = 200;
        q = c->buffer;
1390 1391 1392 1393 1394 1395
        q += snprintf(q, c->buffer_size,
                      "HTTP/1.0 200 Server too busy\r\n"
                      "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"
1396 1397
                      "<p>The bandwidth being served (including your stream) is %"PRIu64"kbit/sec, "
                      "and this exceeds the limit of %"PRIu64"kbit/sec.</p>\r\n"
1398
                      "</body></html>\r\n", current_bandwidth, max_bandwidth);
1399 1400 1401 1402 1403 1404
        /* prepare output buffer */
        c->buffer_ptr = c->buffer;
        c->buffer_end = q;
        c->state = HTTPSTATE_SEND_HEADER;
        return 0;
    }
1405

1406
    if (redir_type != REDIR_NONE) {
1407
        char *hostinfo = 0;
1408

1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438
        for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
            if (strncasecmp(p, "Host:", 5) == 0) {
                hostinfo = p + 5;
                break;
            }
            p = strchr(p, '\n');
            if (!p)
                break;

            p++;
        }

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

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

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

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

                    c->http_error = 200;
                    q = c->buffer;
1439 1440
                    switch(redir_type) {
                    case REDIR_ASX:
1441 1442 1443 1444 1445 1446 1447 1448
                        q += snprintf(q, c->buffer_size,
                                      "HTTP/1.0 200 ASX Follows\r\n"
                                      "Content-type: video/x-ms-asf\r\n"
                                      "\r\n"
                                      "<ASX Version=\"3\">\r\n"
                                      //"<!-- Autogenerated by ffserver -->\r\n"
                                      "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n"
                                      "</ASX>\r\n", hostbuf, filename, info);
1449 1450
                        break;
                    case REDIR_RAM:
1451 1452 1453 1454 1455 1456
                        q += snprintf(q, c->buffer_size,
                                      "HTTP/1.0 200 RAM Follows\r\n"
                                      "Content-type: audio/x-pn-realaudio\r\n"
                                      "\r\n"
                                      "# Autogenerated by ffserver\r\n"
                                      "http://%s/%s%s\r\n", hostbuf, filename, info);
1457 1458
                        break;
                    case REDIR_ASF:
1459 1460 1461 1462 1463 1464
                        q += snprintf(q, c->buffer_size,
                                      "HTTP/1.0 200 ASF Redirect follows\r\n"
                                      "Content-type: video/x-ms-asf\r\n"
                                      "\r\n"
                                      "[Reference]\r\n"
                                      "Ref1=http://%s/%s%s\r\n", hostbuf, filename, info);
1465 1466 1467 1468 1469
                        break;
                    case REDIR_RTSP:
                        {
                            char hostname[256], *p;
                            /* extract only hostname */
1470
                            av_strlcpy(hostname, hostbuf, sizeof(hostname));
1471 1472 1473
                            p = strrchr(hostname, ':');
                            if (p)
                                *p = '\0';
1474 1475 1476 1477 1478 1479
                            q += snprintf(q, c->buffer_size,
                                          "HTTP/1.0 200 RTSP Redirect follows\r\n"
                                          /* XXX: incorrect mime type ? */
                                          "Content-type: application/x-rtsp\r\n"
                                          "\r\n"
                                          "rtsp://%s:%d/%s\r\n", hostname, ntohs(my_rtsp_addr.sin_port), filename);
1480 1481 1482 1483
                        }
                        break;
                    case REDIR_SDP:
                        {
1484
                            uint8_t *sdp_data;
1485 1486 1487
                            int sdp_data_size, len;
                            struct sockaddr_in my_addr;

1488 1489 1490 1491
                            q += snprintf(q, c->buffer_size,
                                          "HTTP/1.0 200 OK\r\n"
                                          "Content-type: application/sdp\r\n"
                                          "\r\n");
1492 1493 1494

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

1496
                            /* XXX: should use a dynamic buffer */
1497 1498
                            sdp_data_size = prepare_sdp_description(stream,
                                                                    &sdp_data,
1499 1500 1501 1502 1503 1504 1505 1506 1507 1508
                                                                    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:
1509
                        abort();
1510
                        break;
1511
                    }
1512 1513 1514 1515 1516 1517 1518 1519 1520 1521

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

1522
        snprintf(msg, sizeof(msg), "ASX/RAM file not handled");
1523
        goto send_error;
Fabrice Bellard's avatar
Fabrice Bellard committed
1524 1525
    }

1526
    stream->conns_served++;
1527

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

1530
    if (c->post) {
Fabrice Bellard's avatar
Fabrice Bellard committed
1531 1532
        /* if post, it means a feed is being sent */
        if (!stream->is_feed) {
Diego Biurrun's avatar
Diego Biurrun committed
1533 1534
            /* However it might be a status report from WMP! Let us log the
             * data as it might come in handy one day. */
1535
            char *logline = 0;
1536
            int client_id = 0;
1537

1538 1539 1540 1541 1542
            for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
                if (strncasecmp(p, "Pragma: log-line=", 17) == 0) {
                    logline = p;
                    break;
                }
1543
                if (strncasecmp(p, "Pragma: client-id=", 18) == 0)
1544
                    client_id = strtol(p + 18, 0, 10);
1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559
                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
1560
                    http_log("%.*s\n", (int) (eol - logline), logline);
1561 1562 1563
                    c->suppress_log = 1;
                }
            }
1564

1565 1566
#ifdef DEBUG_WMP
            http_log("\nGot request:\n%s\n", c->buffer);
1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577
#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;
                }

1578 1579
                if (wmpc && modify_current_stream(wmpc, ratebuf))
                    wmpc->switch_pending = 1;
1580
            }
1581

1582
            snprintf(msg, sizeof(msg), "POST command not handled");
1583
            c->stream = 0;
Fabrice Bellard's avatar
Fabrice Bellard committed
1584 1585 1586
            goto send_error;
        }
        if (http_start_receive_data(c) < 0) {
1587
            snprintf(msg, sizeof(msg), "could not open feed");
Fabrice Bellard's avatar
Fabrice Bellard committed
1588 1589 1590 1591 1592 1593 1594
            goto send_error;
        }
        c->http_error = 0;
        c->state = HTTPSTATE_RECEIVE_DATA;
        return 0;
    }

1595
#ifdef DEBUG_WMP
1596
    if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0)
1597
        http_log("\nGot request:\n%s\n", c->buffer);
1598 1599
#endif

Fabrice Bellard's avatar
Fabrice Bellard committed
1600
    if (c->stream->stream_type == STREAM_TYPE_STATUS)
1601
        goto send_status;
Fabrice Bellard's avatar
Fabrice Bellard committed
1602 1603 1604

    /* open input stream */
    if (open_input_stream(c, info) < 0) {
1605
        snprintf(msg, sizeof(msg), "Input stream corresponding to '%s' not found", url);
Fabrice Bellard's avatar
Fabrice Bellard committed
1606 1607 1608 1609 1610
        goto send_error;
    }

    /* prepare http header */
    q = c->buffer;
1611
    q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
Fabrice Bellard's avatar
Fabrice Bellard committed
1612 1613
    mime_type = c->stream->fmt->mime_type;
    if (!mime_type)
1614
        mime_type = "application/x-octet-stream";
1615
    q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Pragma: no-cache\r\n");
Fabrice Bellard's avatar
Fabrice Bellard committed
1616 1617

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

1621
        c->wmp_client_id = av_lfg_get(&random_state);
1622

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

Fabrice Bellard's avatar
Fabrice Bellard committed
1628 1629 1630 1631 1632 1633 1634 1635 1636
    /* 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;
1637 1638 1639 1640 1641 1642 1643 1644
    q += snprintf(q, c->buffer_size,
                  "HTTP/1.0 404 Not Found\r\n"
                  "Content-type: text/html\r\n"
                  "\r\n"
                  "<HTML>\n"
                  "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n"
                  "<BODY>%s</BODY>\n"
                  "</HTML>\n", msg);
Fabrice Bellard's avatar
Fabrice Bellard committed
1645 1646 1647 1648 1649
    /* prepare output buffer */
    c->buffer_ptr = c->buffer;
    c->buffer_end = q;
    c->state = HTTPSTATE_SEND_HEADER;
    return 0;
1650 1651
 send_status:
    compute_status(c);
Fabrice Bellard's avatar
Fabrice Bellard committed
1652 1653 1654 1655 1656 1657
    c->http_error = 200; /* horrible : we use this value to avoid
                            going to the send data state */
    c->state = HTTPSTATE_SEND_HEADER;
    return 0;
}

1658
static void fmt_bytecount(ByteIOContext *pb, int64_t count)
1659 1660 1661 1662
{
    static const char *suffix = " kMGTP";
    const char *s;

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

1665
    url_fprintf(pb, "%"PRId64"%c", count, *s);
1666 1667
}

1668
static void compute_status(HTTPContext *c)
Fabrice Bellard's avatar
Fabrice Bellard committed
1669 1670 1671
{
    HTTPContext *c1;
    FFStream *stream;
1672
    char *p;
Fabrice Bellard's avatar
Fabrice Bellard committed
1673
    time_t ti;
1674
    int i, len;
1675
    ByteIOContext *pb;
1676

1677
    if (url_open_dyn_buf(&pb) < 0) {
1678
        /* XXX: return an error ? */
1679
        c->buffer_ptr = c->buffer;
1680 1681
        c->buffer_end = c->buffer;
        return;
1682
    }
Fabrice Bellard's avatar
Fabrice Bellard committed
1683

1684 1685 1686 1687
    url_fprintf(pb, "HTTP/1.0 200 OK\r\n");
    url_fprintf(pb, "Content-type: %s\r\n", "text/html");
    url_fprintf(pb, "Pragma: no-cache\r\n");
    url_fprintf(pb, "\r\n");
1688

1689
    url_fprintf(pb, "<HTML><HEAD><TITLE>%s Status</TITLE>\n", program_name);
1690
    if (c->stream->feed_filename[0])
1691 1692
        url_fprintf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
    url_fprintf(pb, "</HEAD>\n<BODY>");
1693
    url_fprintf(pb, "<H1>%s Status</H1>\n", program_name);
Fabrice Bellard's avatar
Fabrice Bellard committed
1694
    /* format status */
1695 1696 1697
    url_fprintf(pb, "<H2>Available Streams</H2>\n");
    url_fprintf(pb, "<TABLE cellspacing=0 cellpadding=4>\n");
    url_fprintf(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
1698 1699
    stream = first_stream;
    while (stream != NULL) {
1700 1701 1702
        char sfilename[1024];
        char *eosf;

1703
        if (stream->feed != stream) {
1704
            av_strlcpy(sfilename, stream->filename, sizeof(sfilename) - 10);
1705 1706
            eosf = sfilename + strlen(sfilename);
            if (eosf - sfilename >= 4) {
1707
                if (strcmp(eosf - 4, ".asf") == 0)
1708
                    strcpy(eosf - 4, ".asx");
1709
                else if (strcmp(eosf - 3, ".rm") == 0)
1710
                    strcpy(eosf - 3, ".ram");
1711
                else if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
1712 1713 1714
                    /* generate a sample RTSP director if
                       unicast. Generate an SDP redirector if
                       multicast */
1715 1716 1717
                    eosf = strrchr(sfilename, '.');
                    if (!eosf)
                        eosf = sfilename + strlen(sfilename);
1718 1719 1720 1721
                    if (stream->is_multicast)
                        strcpy(eosf, ".sdp");
                    else
                        strcpy(eosf, ".rtsp");
1722
                }
1723
            }
1724 1725

            url_fprintf(pb, "<TR><TD><A HREF=\"/%s\">%s</A> ",
1726
                         sfilename, stream->filename);
1727
            url_fprintf(pb, "<td align=right> %d <td align=right> ",
1728
                        stream->conns_served);
1729
            fmt_bytecount(pb, stream->bytes_served);
1730
            switch(stream->stream_type) {
1731
            case STREAM_TYPE_LIVE: {
1732 1733
                    int audio_bit_rate = 0;
                    int video_bit_rate = 0;
Zdenek Kabelac's avatar
Zdenek Kabelac committed
1734 1735 1736 1737
                    const char *audio_codec_name = "";
                    const char *video_codec_name = "";
                    const char *audio_codec_name_extra = "";
                    const char *video_codec_name_extra = "";
1738 1739 1740

                    for(i=0;i<stream->nb_streams;i++) {
                        AVStream *st = stream->streams[i];
1741 1742
                        AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
                        switch(st->codec->codec_type) {
1743
                        case CODEC_TYPE_AUDIO:
1744
                            audio_bit_rate += st->codec->bit_rate;
1745 1746 1747 1748 1749 1750 1751
                            if (codec) {
                                if (*audio_codec_name)
                                    audio_codec_name_extra = "...";
                                audio_codec_name = codec->name;
                            }
                            break;
                        case CODEC_TYPE_VIDEO:
1752
                            video_bit_rate += st->codec->bit_rate;
1753 1754 1755 1756 1757 1758
                            if (codec) {
                                if (*video_codec_name)
                                    video_codec_name_extra = "...";
                                video_codec_name = codec->name;
                            }
                            break;
1759
                        case CODEC_TYPE_DATA:
1760
                            video_bit_rate += st->codec->bit_rate;
1761
                            break;
1762
                        default:
1763
                            abort();
1764
                        }
Fabrice Bellard's avatar
Fabrice Bellard committed
1765
                    }
1766
                    url_fprintf(pb, "<TD align=center> %s <TD align=right> %d <TD align=right> %d <TD> %s %s <TD align=right> %d <TD> %s %s",
1767
                                 stream->fmt->name,
1768
                                 stream->bandwidth,
1769 1770
                                 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
                                 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
1771
                    if (stream->feed)
1772
                        url_fprintf(pb, "<TD>%s", stream->feed->filename);
1773
                    else
1774 1775
                        url_fprintf(pb, "<TD>%s", stream->feed_filename);
                    url_fprintf(pb, "\n");
Fabrice Bellard's avatar
Fabrice Bellard committed
1776
                }
1777 1778
                break;
            default:
1779
                url_fprintf(pb, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
1780
                break;
Fabrice Bellard's avatar
Fabrice Bellard committed
1781 1782 1783 1784
            }
        }
        stream = stream->next;
    }
1785
    url_fprintf(pb, "</TABLE>\n");
1786 1787 1788 1789

    stream = first_stream;
    while (stream != NULL) {
        if (stream->feed == stream) {
1790
            url_fprintf(pb, "<h2>Feed %s</h2>", stream->filename);
1791
            if (stream->pid) {
1792
                url_fprintf(pb, "Running as pid %d.\n", stream->pid);
1793

1794 1795 1796 1797 1798 1799
#if defined(linux) && !defined(CONFIG_NOCUTILS)
                {
                    FILE *pid_stat;
                    char ps_cmd[64];

                    /* This is somewhat linux specific I guess */
1800 1801
                    snprintf(ps_cmd, sizeof(ps_cmd),
                             "ps -o \"%%cpu,cputime\" --no-headers %d",
1802
                             stream->pid);
1803

1804 1805 1806 1807
                    pid_stat = popen(ps_cmd, "r");
                    if (pid_stat) {
                        char cpuperc[10];
                        char cpuused[64];
1808 1809

                        if (fscanf(pid_stat, "%10s %64s", cpuperc,
1810 1811 1812 1813 1814
                                   cpuused) == 2) {
                            url_fprintf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
                                         cpuperc, cpuused);
                        }
                        fclose(pid_stat);
1815 1816 1817 1818
                    }
                }
#endif

1819
                url_fprintf(pb, "<p>");
1820
            }
1821
            url_fprintf(pb, "<table cellspacing=0 cellpadding=4><tr><th>Stream<th>type<th>kbits/s<th align=left>codec<th align=left>Parameters\n");
1822 1823 1824

            for (i = 0; i < stream->nb_streams; i++) {
                AVStream *st = stream->streams[i];
1825
                AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1826
                const char *type = "unknown";
1827 1828 1829
                char parameters[64];

                parameters[0] = 0;
1830

1831
                switch(st->codec->codec_type) {
1832 1833
                case CODEC_TYPE_AUDIO:
                    type = "audio";
1834
                    snprintf(parameters, sizeof(parameters), "%d channel(s), %d Hz", st->codec->channels, st->codec->sample_rate);
1835 1836 1837
                    break;
                case CODEC_TYPE_VIDEO:
                    type = "video";
1838 1839
                    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);
1840 1841
                    break;
                default:
1842
                    abort();
1843
                }
1844
                url_fprintf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
1845
                        i, type, st->codec->bit_rate/1000, codec ? codec->name : "", parameters);
1846
            }
1847
            url_fprintf(pb, "</table>\n");
1848

1849
        }
1850 1851
        stream = stream->next;
    }
1852

Fabrice Bellard's avatar
Fabrice Bellard committed
1853 1854 1855 1856 1857
#if 0
    {
        float avg;
        AVCodecContext *enc;
        char buf[1024];
1858

Fabrice Bellard's avatar
Fabrice Bellard committed
1859 1860 1861
        /* feed status */
        stream = first_feed;
        while (stream != NULL) {
1862 1863 1864
            url_fprintf(pb, "<H1>Feed '%s'</H1>\n", stream->filename);
            url_fprintf(pb, "<TABLE>\n");
            url_fprintf(pb, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n");
Fabrice Bellard's avatar
Fabrice Bellard committed
1865 1866 1867
            for(i=0;i<stream->nb_streams;i++) {
                AVStream *st = stream->streams[i];
                FeedData *fdata = st->priv_data;
1868
                enc = st->codec;
1869

Fabrice Bellard's avatar
Fabrice Bellard committed
1870 1871 1872 1873
                avcodec_string(buf, sizeof(buf), enc);
                avg = fdata->avg_frame_size * (float)enc->rate * 8.0;
                if (enc->codec->type == CODEC_TYPE_AUDIO && enc->frame_size > 0)
                    avg /= enc->frame_size;
1874
                url_fprintf(pb, "<TR><TD>%s <TD> %d <TD> %"PRId64" <TD> %0.1f\n",
Fabrice Bellard's avatar
Fabrice Bellard committed
1875 1876
                             buf, enc->frame_number, fdata->data_count, avg / 1000.0);
            }
1877
            url_fprintf(pb, "</TABLE>\n");
Fabrice Bellard's avatar
Fabrice Bellard committed
1878 1879 1880 1881 1882 1883
            stream = stream->next_feed;
        }
    }
#endif

    /* connection status */
1884
    url_fprintf(pb, "<H2>Connection Status</H2>\n");
Fabrice Bellard's avatar
Fabrice Bellard committed
1885

1886
    url_fprintf(pb, "Number of connections: %d / %d<BR>\n",
Fabrice Bellard's avatar
Fabrice Bellard committed
1887 1888
                 nb_connections, nb_max_connections);

1889
    url_fprintf(pb, "Bandwidth in use: %"PRIu64"k / %"PRIu64"k<BR>\n",
1890
                 current_bandwidth, max_bandwidth);
1891

1892 1893
    url_fprintf(pb, "<TABLE>\n");
    url_fprintf(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
1894 1895
    c1 = first_http_ctx;
    i = 0;
1896
    while (c1 != NULL) {
1897 1898 1899 1900
        int bitrate;
        int j;

        bitrate = 0;
1901 1902
        if (c1->stream) {
            for (j = 0; j < c1->stream->nb_streams; j++) {
1903
                if (!c1->stream->feed)
1904
                    bitrate += c1->stream->streams[j]->codec->bit_rate;
1905 1906
                else if (c1->feed_streams[j] >= 0)
                    bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec->bit_rate;
1907 1908 1909
            }
        }

Fabrice Bellard's avatar
Fabrice Bellard committed
1910 1911
        i++;
        p = inet_ntoa(c1->from_addr.sin_addr);
1912 1913 1914
        url_fprintf(pb, "<TR><TD><B>%d</B><TD>%s%s<TD>%s<TD>%s<TD>%s<td align=right>",
                    i,
                    c1->stream ? c1->stream->filename : "",
1915
                    c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
1916
                    p,
1917 1918 1919 1920 1921 1922 1923 1924
                    c1->protocol,
                    http_state[c1->state]);
        fmt_bytecount(pb, bitrate);
        url_fprintf(pb, "<td align=right>");
        fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
        url_fprintf(pb, "<td align=right>");
        fmt_bytecount(pb, c1->data_count);
        url_fprintf(pb, "\n");
Fabrice Bellard's avatar
Fabrice Bellard committed
1925 1926
        c1 = c1->next;
    }
1927
    url_fprintf(pb, "</TABLE>\n");
1928

Fabrice Bellard's avatar
Fabrice Bellard committed
1929 1930 1931
    /* date */
    ti = time(NULL);
    p = ctime(&ti);
1932 1933
    url_fprintf(pb, "<HR size=1 noshade>Generated at %s", p);
    url_fprintf(pb, "</BODY>\n</HTML>\n");
Fabrice Bellard's avatar
Fabrice Bellard committed
1934

1935 1936 1937
    len = url_close_dyn_buf(pb, &c->pb_buffer);
    c->buffer_ptr = c->pb_buffer;
    c->buffer_end = c->pb_buffer + len;
Fabrice Bellard's avatar
Fabrice Bellard committed
1938 1939
}

1940 1941
/* check if the parser needs to be opened for stream i */
static void open_parser(AVFormatContext *s, int i)
Fabrice Bellard's avatar
Fabrice Bellard committed
1942
{
1943 1944
    AVStream *st = s->streams[i];
    AVCodec *codec;
1945

1946 1947
    if (!st->codec->codec) {
        codec = avcodec_find_decoder(st->codec->codec_id);
1948
        if (codec && (codec->capabilities & CODEC_CAP_PARSE_ONLY)) {
1949
            st->codec->parse_only = 1;
1950
            if (avcodec_open(st->codec, codec) < 0)
1951
                st->codec->parse_only = 0;
1952 1953
        }
    }
Fabrice Bellard's avatar
Fabrice Bellard committed
1954 1955 1956 1957 1958 1959 1960
}

static int open_input_stream(HTTPContext *c, const char *info)
{
    char buf[128];
    char input_filename[1024];
    AVFormatContext *s;
1961
    int buf_size, i, ret;
1962
    int64_t stream_pos;
Fabrice Bellard's avatar
Fabrice Bellard committed
1963 1964 1965 1966 1967 1968

    /* find file name */
    if (c->stream->feed) {
        strcpy(input_filename, c->stream->feed->feed_filename);
        buf_size = FFM_PACKET_SIZE;
        /* compute position (absolute time) */
1969
        if (find_info_tag(buf, sizeof(buf), "date", info)) {
Fabrice Bellard's avatar
Fabrice Bellard committed
1970
            stream_pos = parse_date(buf, 0);
1971 1972
            if (stream_pos == INT64_MIN)
                return -1;
1973
        } else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
1974
            int prebuffer = strtol(buf, 0, 10);
1975
            stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
1976
        } else
1977
            stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
Fabrice Bellard's avatar
Fabrice Bellard committed
1978 1979 1980 1981
    } else {
        strcpy(input_filename, c->stream->feed_filename);
        buf_size = 0;
        /* compute position (relative time) */
1982
        if (find_info_tag(buf, sizeof(buf), "date", info)) {
Fabrice Bellard's avatar
Fabrice Bellard committed
1983
            stream_pos = parse_date(buf, 1);
1984 1985
            if (stream_pos == INT64_MIN)
                return -1;
1986
        } else
Fabrice Bellard's avatar
Fabrice Bellard committed
1987 1988 1989 1990 1991
            stream_pos = 0;
    }
    if (input_filename[0] == '\0')
        return -1;

1992 1993
#if 0
    { time_t when = stream_pos / 1000000;
1994
    http_log("Stream pos = %"PRId64", time=%s", stream_pos, ctime(&when));
1995 1996 1997
    }
#endif

Fabrice Bellard's avatar
Fabrice Bellard committed
1998
    /* open stream */
1999 2000 2001
    if ((ret = av_open_input_file(&s, input_filename, c->stream->ifmt,
                                  buf_size, c->stream->ap_in)) < 0) {
        http_log("could not open %s: %d\n", input_filename, ret);
Fabrice Bellard's avatar
Fabrice Bellard committed
2002
        return -1;
2003
    }
2004
    s->flags |= AVFMT_FLAG_GENPTS;
Fabrice Bellard's avatar
Fabrice Bellard committed
2005
    c->fmt_in = s;
2006
    if (strcmp(s->iformat->name, "ffm") && av_find_stream_info(c->fmt_in) < 0) {
2007 2008 2009 2010
        http_log("Could not find stream info '%s'\n", input_filename);
        av_close_input_file(s);
        return -1;
    }
2011

2012 2013 2014 2015 2016 2017 2018 2019
    /* open each parser */
    for(i=0;i<s->nb_streams;i++)
        open_parser(s, i);

    /* 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++) {
2020
        if (c->pts_stream_index == 0 &&
2021
            c->stream->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO) {
2022 2023 2024
            c->pts_stream_index = i;
        }
    }
Fabrice Bellard's avatar
Fabrice Bellard committed
2025

2026
#if 1
2027
    if (c->fmt_in->iformat->read_seek)
2028
        av_seek_frame(c->fmt_in, -1, stream_pos, 0);
2029
#endif
2030 2031 2032
    /* 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
2033 2034 2035
    return 0;
}

2036 2037
/* return the server clock (in us) */
static int64_t get_server_clock(HTTPContext *c)
2038
{
2039
    /* compute current pts value from system time */
2040
    return (cur_time - c->start_time) * 1000;
2041 2042
}

2043 2044 2045
/* return the estimated time at which the current packet must be sent
   (in us) */
static int64_t get_packet_send_clock(HTTPContext *c)
2046
{
2047
    int bytes_left, bytes_sent, frame_bytes;
2048

2049
    frame_bytes = c->cur_frame_bytes;
2050
    if (frame_bytes <= 0)
2051
        return c->cur_pts;
2052
    else {
2053 2054 2055
        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;
2056 2057 2058 2059 2060 2061 2062 2063 2064
    }
}


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

2065
    av_freep(&c->pb_buffer);
2066 2067 2068
    switch(c->state) {
    case HTTPSTATE_SEND_DATA_HEADER:
        memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
2069 2070 2071 2072
        av_metadata_set(&c->fmt_ctx.metadata, "author"   ,c->stream->author);
        av_metadata_set(&c->fmt_ctx.metadata, "comment"  ,c->stream->comment);
        av_metadata_set(&c->fmt_ctx.metadata, "copyright",c->stream->copyright);
        av_metadata_set(&c->fmt_ctx.metadata, "title"    ,c->stream->title);
2073

2074
        for(i=0;i<c->stream->nb_streams;i++) {
2075
            AVStream *st;
2076
            AVStream *src;
2077 2078 2079
            st = av_mallocz(sizeof(AVStream));
            c->fmt_ctx.streams[i] = st;
            /* if file or feed, then just take streams from FFStream struct */
2080
            if (!c->stream->feed ||
2081
                c->stream->feed == c->stream)
2082
                src = c->stream->streams[i];
2083
            else
2084 2085
                src = c->stream->feed->streams[c->stream->feed_streams[i]];

2086 2087
            *st = *src;
            st->priv_data = 0;
2088
            st->codec->frame_number = 0; /* XXX: should be done in
2089 2090
                                           AVStream, not in codec */
        }
2091 2092 2093 2094
        /* set output format parameters */
        c->fmt_ctx.oformat = c->stream->fmt;
        c->fmt_ctx.nb_streams = c->stream->nb_streams;

2095 2096 2097 2098 2099 2100 2101
        c->got_key_frame = 0;

        /* prepare header and save header data in a stream */
        if (url_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
            /* XXX: potential leak */
            return -1;
        }
2102
        c->fmt_ctx.pb->is_streamed = 1;
2103

2104 2105 2106 2107 2108 2109 2110 2111
        /*
         * 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.preload   = (int)(0.5*AV_TIME_BASE);
        c->fmt_ctx.max_delay = (int)(0.7*AV_TIME_BASE);

2112
        av_set_parameters(&c->fmt_ctx, NULL);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2113 2114
        if (av_write_header(&c->fmt_ctx) < 0) {
            http_log("Error writing output header\n");
2115
            return -1;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2116
        }
2117

2118
        len = url_close_dyn_buf(c->fmt_ctx.pb, &c->pb_buffer);
2119 2120 2121 2122
        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
2123 2124 2125 2126
        c->last_packet_sent = 0;
        break;
    case HTTPSTATE_SEND_DATA:
        /* find a new packet */
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145
        /* 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:
            if (av_read_frame(c->fmt_in, &pkt) < 0) {
                if (c->stream->feed && c->stream->feed->feed_opened) {
                    /* 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 */
2146
                } else {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2147 2148 2149 2150 2151 2152 2153 2154 2155 2156
                    if (c->stream->loop) {
                        av_close_input_file(c->fmt_in);
                        c->fmt_in = NULL;
                        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;
2157
                    }
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2158 2159
                }
            } else {
2160
                int source_index = pkt.stream_index;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2161 2162 2163 2164 2165 2166 2167 2168 2169 2170
                /* 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;
2171
                        for(i=0;i<c->stream->nb_streams;i++) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2172
                            if (c->switch_feed_streams[i] == pkt.stream_index)
2173
                                if (pkt.flags & PKT_FLAG_KEY)
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2174 2175 2176
                                    do_switch_stream(c, i);
                            if (c->switch_feed_streams[i] >= 0)
                                c->switch_pending = 1;
2177
                        }
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2178 2179 2180
                    }
                    for(i=0;i<c->stream->nb_streams;i++) {
                        if (c->feed_streams[i] == pkt.stream_index) {
2181
                            AVStream *st = c->fmt_in->streams[source_index];
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2182
                            pkt.stream_index = i;
2183 2184 2185
                            if (pkt.flags & PKT_FLAG_KEY &&
                                (st->codec->codec_type == CODEC_TYPE_VIDEO ||
                                 c->stream->nb_streams == 1))
2186 2187
                                c->got_key_frame = 1;
                            if (!c->stream->send_on_key || c->got_key_frame)
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2188 2189 2190 2191 2192
                                goto send_it;
                        }
                    }
                } else {
                    AVCodecContext *codec;
2193 2194 2195
                    AVStream *ist, *ost;
                send_it:
                    ist = c->fmt_in->streams[source_index];
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2196 2197 2198 2199 2200
                    /* 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
2201 2202 2203 2204
                        c->cur_pts = av_rescale_q(pkt.dts, ist->time_base, AV_TIME_BASE_Q);
                        if (ist->start_time != AV_NOPTS_VALUE)
                            c->cur_pts -= av_rescale_q(ist->start_time, ist->time_base, AV_TIME_BASE_Q);
                        c->cur_frame_duration = av_rescale_q(pkt.duration, ist->time_base, AV_TIME_BASE_Q);
2205
#if 0
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2206 2207 2208 2209 2210 2211
                        printf("index=%d pts=%0.3f duration=%0.6f\n",
                               pkt.stream_index,
                               (double)c->cur_pts /
                               AV_TIME_BASE,
                               (double)c->cur_frame_duration /
                               AV_TIME_BASE);
2212
#endif
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2213 2214 2215 2216
                        /* find RTP context */
                        c->packet_stream_index = pkt.stream_index;
                        ctx = c->rtp_ctx[c->packet_stream_index];
                        if(!ctx) {
2217
                            av_free_packet(&pkt);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2218
                            break;
2219
                        }
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2220 2221 2222 2223 2224 2225
                        codec = ctx->streams[0]->codec;
                        /* only one stream per RTP connection */
                        pkt.stream_index = 0;
                    } else {
                        ctx = &c->fmt_ctx;
                        /* Fudge here */
2226
                        codec = ctx->streams[pkt.stream_index]->codec;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2227 2228 2229 2230
                    }

                    if (c->is_packetized) {
                        int max_packet_size;
2231
                        if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP)
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242
                            max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
                        else
                            max_packet_size = url_get_max_packet_size(c->rtp_handles[c->packet_stream_index]);
                        ret = url_open_dyn_packet_buf(&ctx->pb, max_packet_size);
                    } else {
                        ret = url_open_dyn_buf(&ctx->pb);
                    }
                    if (ret < 0) {
                        /* XXX: potential leak */
                        return -1;
                    }
2243 2244
                    ost = ctx->streams[pkt.stream_index];

2245
                    ctx->pb->is_streamed = 1;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2246
                    if (pkt.dts != AV_NOPTS_VALUE)
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2247
                        pkt.dts = av_rescale_q(pkt.dts, ist->time_base, ost->time_base);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2248
                    if (pkt.pts != AV_NOPTS_VALUE)
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2249 2250
                        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);
2251 2252
                    if (av_write_frame(ctx, &pkt) < 0) {
                        http_log("Error writing frame to output\n");
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2253
                        c->state = HTTPSTATE_SEND_DATA_TRAILER;
2254
                    }
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2255 2256 2257 2258 2259 2260 2261 2262 2263 2264

                    len = url_close_dyn_buf(ctx->pb, &c->pb_buffer);
                    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;
2265
                    }
Fabrice Bellard's avatar
Fabrice Bellard committed
2266
                }
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2267
                av_free_packet(&pkt);
Fabrice Bellard's avatar
Fabrice Bellard committed
2268
            }
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2269
        }
Fabrice Bellard's avatar
Fabrice Bellard committed
2270 2271 2272 2273
        break;
    default:
    case HTTPSTATE_SEND_DATA_TRAILER:
        /* last packet test ? */
2274
        if (c->last_packet_sent || c->is_packetized)
Fabrice Bellard's avatar
Fabrice Bellard committed
2275
            return -1;
2276
        ctx = &c->fmt_ctx;
Fabrice Bellard's avatar
Fabrice Bellard committed
2277
        /* prepare header */
2278 2279 2280 2281
        if (url_open_dyn_buf(&ctx->pb) < 0) {
            /* XXX: potential leak */
            return -1;
        }
2282
        c->fmt_ctx.pb->is_streamed = 1;
2283
        av_write_trailer(ctx);
2284
        len = url_close_dyn_buf(ctx->pb, &c->pb_buffer);
2285 2286 2287
        c->buffer_ptr = c->pb_buffer;
        c->buffer_end = c->pb_buffer + len;

Fabrice Bellard's avatar
Fabrice Bellard committed
2288 2289 2290 2291 2292 2293 2294
        c->last_packet_sent = 1;
        break;
    }
    return 0;
}

/* should convert the format at the same time */
2295 2296
/* send data starting at c->buffer_ptr to the output connection
   (either UDP or TCP connection) */
2297
static int http_send_data(HTTPContext *c)
Fabrice Bellard's avatar
Fabrice Bellard committed
2298
{
2299
    int len, ret;
Fabrice Bellard's avatar
Fabrice Bellard committed
2300

2301 2302 2303 2304 2305
    for(;;) {
        if (c->buffer_ptr >= c->buffer_end) {
            ret = http_prepare_data(c);
            if (ret < 0)
                return -1;
2306
            else if (ret != 0)
2307 2308
                /* state change requested */
                break;
2309
        } else {
2310 2311 2312 2313 2314 2315 2316
            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;
2317 2318
                    return 0;
                }
2319 2320 2321 2322 2323 2324
                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;
2325 2326 2327 2328 2329 2330 2331 2332 2333 2334
                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;

2335
                if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP) {
2336
                    /* RTP packets are sent inside the RTSP TCP connection */
2337
                    ByteIOContext *pb;
2338 2339 2340
                    int interleaved_index, size;
                    uint8_t header[4];
                    HTTPContext *rtsp_c;
2341

2342 2343 2344 2345 2346
                    rtsp_c = c->rtsp_c;
                    /* if no RTSP connection left, error */
                    if (!rtsp_c)
                        return -1;
                    /* if already sending something, then wait. */
2347
                    if (rtsp_c->state != RTSPSTATE_WAIT_REQUEST)
2348
                        break;
2349
                    if (url_open_dyn_buf(&pb) < 0)
2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367
                        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;
                    put_buffer(pb, header, 4);
                    /* write RTP packet data */
                    c->buffer_ptr += 4;
                    put_buffer(pb, c->buffer_ptr, len);
                    size = url_close_dyn_buf(pb, &c->packet_buffer);
                    /* prepare asynchronous TCP sending */
                    rtsp_c->packet_buffer_ptr = c->packet_buffer;
                    rtsp_c->packet_buffer_end = c->packet_buffer + size;
2368
                    c->buffer_ptr += len;
2369

2370
                    /* send everything we can NOW */
2371 2372
                    len = send(rtsp_c->fd, rtsp_c->packet_buffer_ptr,
                                rtsp_c->packet_buffer_end - rtsp_c->packet_buffer_ptr, 0);
2373
                    if (len > 0)
2374 2375 2376 2377 2378 2379 2380
                        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;
2381
                    } else
2382 2383 2384 2385
                        /* all data has been sent */
                        av_freep(&c->packet_buffer);
                } else {
                    /* send RTP packet directly in UDP */
2386
                    c->buffer_ptr += 4;
2387
                    url_write(c->rtp_handles[c->packet_stream_index],
2388
                              c->buffer_ptr, len);
2389 2390
                    c->buffer_ptr += len;
                    /* here we continue as we can send several packets per 10 ms slot */
2391 2392 2393
                }
            } else {
                /* TCP data output */
2394
                len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2395
                if (len < 0) {
2396
                    if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2397
                        ff_neterrno() != FF_NETERROR(EINTR))
2398 2399
                        /* error : close connection */
                        return -1;
2400
                    else
2401
                        return 0;
2402
                } else
2403
                    c->buffer_ptr += len;
2404

2405 2406 2407 2408 2409
                c->data_count += len;
                update_datarate(&c->datarate, c->data_count);
                if (c->stream)
                    c->stream->bytes_served += len;
                break;
2410
            }
Fabrice Bellard's avatar
Fabrice Bellard committed
2411
        }
2412
    } /* for(;;) */
Fabrice Bellard's avatar
Fabrice Bellard committed
2413 2414 2415 2416 2417 2418 2419 2420 2421 2422
    return 0;
}

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

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

2423 2424 2425 2426
    /* Don't permit writing to this one */
    if (c->stream->readonly)
        return -1;

Fabrice Bellard's avatar
Fabrice Bellard committed
2427 2428
    /* open feed */
    fd = open(c->stream->feed_filename, O_RDWR);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2429 2430
    if (fd < 0) {
        http_log("Error opening feeder file: %s\n", strerror(errno));
Fabrice Bellard's avatar
Fabrice Bellard committed
2431
        return -1;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2432
    }
Fabrice Bellard's avatar
Fabrice Bellard committed
2433
    c->feed_fd = fd;
2434

2435 2436 2437 2438 2439 2440
    if (c->stream->truncate) {
        /* truncate feed file */
        ffm_write_write_index(c->feed_fd, FFM_PACKET_SIZE);
        ftruncate(c->feed_fd, FFM_PACKET_SIZE);
        http_log("Truncating feed file '%s'\n", c->stream->feed_filename);
    } else {
2441 2442 2443 2444
        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;
        }
2445 2446
    }

2447
    c->stream->feed_write_index = FFMAX(ffm_read_write_index(fd), FFM_PACKET_SIZE);
Fabrice Bellard's avatar
Fabrice Bellard committed
2448 2449 2450 2451 2452 2453 2454 2455 2456
    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;
    return 0;
}
2457

Fabrice Bellard's avatar
Fabrice Bellard committed
2458 2459 2460 2461
static int http_receive_data(HTTPContext *c)
{
    HTTPContext *c1;

2462 2463 2464
    if (c->buffer_end > c->buffer_ptr) {
        int len;

2465
        len = recv(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2466
        if (len < 0) {
2467
            if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2468
                ff_neterrno() != FF_NETERROR(EINTR))
2469 2470
                /* error : close connection */
                goto fail;
2471
        } else if (len == 0)
2472 2473
            /* end of connection : close it */
            goto fail;
2474
        else {
2475 2476
            c->buffer_ptr += len;
            c->data_count += len;
2477
            update_datarate(&c->datarate, c->data_count);
2478 2479 2480
        }
    }

2481 2482 2483 2484 2485 2486 2487 2488
    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
2489
    if (c->buffer_ptr >= c->buffer_end) {
2490
        FFStream *feed = c->stream;
Fabrice Bellard's avatar
Fabrice Bellard committed
2491 2492 2493
        /* a packet has been received : write it in the store, except
           if header */
        if (c->data_count > FFM_PACKET_SIZE) {
2494

2495
            //            printf("writing pos=0x%"PRIx64" size=0x%"PRIx64"\n", feed->feed_write_index, feed->feed_size);
Fabrice Bellard's avatar
Fabrice Bellard committed
2496 2497
            /* XXX: use llseek or url_seek */
            lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2498 2499 2500 2501
            if (write(c->feed_fd, c->buffer, FFM_PACKET_SIZE) < 0) {
                http_log("Error writing to feed file: %s\n", strerror(errno));
                goto fail;
            }
2502

Fabrice Bellard's avatar
Fabrice Bellard committed
2503 2504 2505 2506 2507 2508
            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 */
2509
            if (c->stream->feed_max_size && feed->feed_write_index >= c->stream->feed_max_size)
Fabrice Bellard's avatar
Fabrice Bellard committed
2510 2511 2512
                feed->feed_write_index = FFM_PACKET_SIZE;

            /* write index */
2513 2514 2515 2516
            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
2517 2518 2519

            /* wake up any waiting connections */
            for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2520
                if (c1->state == HTTPSTATE_WAIT_FEED &&
2521
                    c1->stream->feed == c->stream->feed)
Fabrice Bellard's avatar
Fabrice Bellard committed
2522 2523
                    c1->state = HTTPSTATE_SEND_DATA;
            }
2524 2525
        } else {
            /* We have a header in our hands that contains useful data */
2526 2527
            AVFormatContext *s = NULL;
            ByteIOContext *pb;
2528
            AVInputFormat *fmt_in;
2529 2530
            int i;

2531 2532 2533 2534 2535
            /* use feed output format name to find corresponding input format */
            fmt_in = av_find_input_format(feed->fmt->name);
            if (!fmt_in)
                goto fail;

2536 2537 2538
            url_open_buf(&pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY);
            pb->is_streamed = 1;

2539 2540 2541 2542
            if (av_open_input_stream(&s, pb, c->stream->feed_filename, fmt_in, NULL) < 0) {
                av_free(pb);
                goto fail;
            }
2543 2544

            /* Now we have the actual streams */
2545 2546
            if (s->nb_streams != feed->nb_streams) {
                av_close_input_stream(s);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2547
                av_free(pb);
2548 2549
                http_log("Feed '%s' stream number does not match registered feed\n",
                         c->stream->feed_filename);
2550 2551
                goto fail;
            }
2552

2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564
            for (i = 0; i < s->nb_streams; i++) {
                AVStream *fst = feed->streams[i];
                AVStream *st = s->streams[i];
                memcpy(fst->codec, st->codec, sizeof(AVCodecContext));
                if (fst->codec->extradata_size) {
                    fst->codec->extradata = av_malloc(fst->codec->extradata_size);
                    if (!fst->codec->extradata)
                        goto fail;
                    memcpy(fst->codec->extradata, st->codec->extradata,
                           fst->codec->extradata_size);
                }
            }
2565 2566

            av_close_input_stream(s);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
2567
            av_free(pb);
Fabrice Bellard's avatar
Fabrice Bellard committed
2568 2569 2570 2571 2572 2573 2574 2575
        }
        c->buffer_ptr = c->buffer;
    }

    return 0;
 fail:
    c->stream->feed_opened = 0;
    close(c->feed_fd);
2576 2577 2578 2579 2580 2581
    /* 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
2582 2583 2584
    return -1;
}

2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595
/********************************************************************/
/* RTSP handling */

static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
{
    const char *str;
    time_t ti;
    char *p;
    char buf2[32];

    switch(error_number) {
2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628
    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;
2629 2630 2631 2632
    default:
        str = "Unknown Error";
        break;
    }
2633

2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660
    url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
    url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);

    /* output GMT time */
    ti = time(NULL);
    p = ctime(&ti);
    strcpy(buf2, p);
    p = buf2 + strlen(p) - 1;
    if (*p == '\n')
        *p = '\0';
    url_fprintf(c->pb, "Date: %s GMT\r\n", buf2);
}

static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
{
    rtsp_reply_header(c, error_number);
    url_fprintf(c->pb, "\r\n");
}

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;
2661
    RTSPMessageHeader header1, *header = &header1;
2662

2663 2664
    c->buffer_ptr[0] = '\0';
    p = c->buffer;
2665

2666 2667 2668 2669
    get_word(cmd, sizeof(cmd), &p);
    get_word(url, sizeof(url), &p);
    get_word(protocol, sizeof(protocol), &p);

2670 2671 2672
    av_strlcpy(c->method, cmd, sizeof(c->method));
    av_strlcpy(c->url, url, sizeof(c->url));
    av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
2673

2674
    if (url_open_dyn_buf(&c->pb) < 0) {
2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686
        /* 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 */
2687
    memset(header, 0, sizeof(*header));
2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714
    /* skip to next line */
    while (*p != '\n' && *p != '\0')
        p++;
    if (*p == '\n')
        p++;
    while (*p != '\0') {
        p1 = strchr(p, '\n');
        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';
        rtsp_parse_line(header, line);
        p = p1 + 1;
    }

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

2715
    if (!strcmp(cmd, "DESCRIBE"))
2716
        rtsp_cmd_describe(c, url);
2717
    else if (!strcmp(cmd, "OPTIONS"))
2718
        rtsp_cmd_options(c, url);
2719
    else if (!strcmp(cmd, "SETUP"))
2720
        rtsp_cmd_setup(c, url, header);
2721
    else if (!strcmp(cmd, "PLAY"))
2722
        rtsp_cmd_play(c, url, header);
2723
    else if (!strcmp(cmd, "PAUSE"))
2724
        rtsp_cmd_pause(c, url, header);
2725
    else if (!strcmp(cmd, "TEARDOWN"))
2726
        rtsp_cmd_teardown(c, url, header);
2727
    else
2728
        rtsp_reply_error(c, RTSP_STATUS_METHOD);
2729

2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742
 the_end:
    len = url_close_dyn_buf(c->pb, &c->pb_buffer);
    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;
}

2743
static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
2744
                                   struct in_addr my_ip)
2745
{
2746 2747 2748
    AVFormatContext *avc;
    AVStream avs[MAX_STREAMS];
    int i;
2749

2750
    avc =  avformat_alloc_context();
2751
    if (avc == NULL) {
2752
        return -1;
2753
    }
2754 2755
    av_metadata_set(&avc->metadata, "title",
                    stream->title[0] ? stream->title : "No Title");
2756 2757 2758 2759 2760 2761
    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);
    }
2762

2763
    for(i = 0; i < stream->nb_streams; i++) {
2764 2765
        avc->streams[i] = &avs[i];
        avc->streams[i]->codec = stream->streams[i]->codec;
2766
    }
2767 2768 2769 2770 2771
    *pbuffer = av_mallocz(2048);
    avf_sdp_create(&avc, 1, *pbuffer, 2048);
    av_free(avc);

    return strlen(*pbuffer);
2772 2773
}

2774 2775 2776 2777 2778 2779 2780 2781 2782
static void rtsp_cmd_options(HTTPContext *c, const char *url)
{
//    rtsp_reply_header(c, RTSP_STATUS_OK);
    url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK");
    url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
    url_fprintf(c->pb, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
    url_fprintf(c->pb, "\r\n");
}

2783 2784 2785 2786 2787
static void rtsp_cmd_describe(HTTPContext *c, const char *url)
{
    FFStream *stream;
    char path1[1024];
    const char *path;
2788
    uint8_t *content;
2789 2790
    int content_length, len;
    struct sockaddr_in my_addr;
2791

2792
    /* find which url is asked */
2793
    url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2794 2795 2796 2797 2798
    path = path1;
    if (*path == '/')
        path++;

    for(stream = first_stream; stream != NULL; stream = stream->next) {
2799 2800
        if (!stream->is_feed &&
            stream->fmt && !strcmp(stream->fmt->name, "rtp") &&
2801 2802 2803 2804 2805 2806 2807 2808 2809 2810
            !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 */
2811 2812 2813 2814 2815

    /* 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);
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
    if (content_length < 0) {
        rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
        return;
    }
    rtsp_reply_header(c, RTSP_STATUS_OK);
    url_fprintf(c->pb, "Content-Type: application/sdp\r\n");
    url_fprintf(c->pb, "Content-Length: %d\r\n", content_length);
    url_fprintf(c->pb, "\r\n");
    put_buffer(c->pb, content, content_length);
}

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

2841
static RTSPTransportField *find_transport(RTSPMessageHeader *h, enum RTSPLowerTransport lower_transport)
2842 2843 2844 2845 2846 2847
{
    RTSPTransportField *th;
    int i;

    for(i=0;i<h->nb_transports;i++) {
        th = &h->transports[i];
2848
        if (th->lower_transport == lower_transport)
2849 2850 2851 2852 2853
            return th;
    }
    return NULL;
}

2854
static void rtsp_cmd_setup(HTTPContext *c, const char *url,
2855
                           RTSPMessageHeader *h)
2856 2857 2858 2859 2860 2861 2862 2863 2864 2865
{
    FFStream *stream;
    int stream_index, port;
    char buf[1024];
    char path1[1024];
    const char *path;
    HTTPContext *rtp_c;
    RTSPTransportField *th;
    struct sockaddr_in dest_addr;
    RTSPActionServerSetup setup;
2866

2867
    /* find which url is asked */
2868
    url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2869 2870 2871 2872 2873 2874
    path = path1;
    if (*path == '/')
        path++;

    /* now check each stream */
    for(stream = first_stream; stream != NULL; stream = stream->next) {
2875 2876
        if (!stream->is_feed &&
            stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
2877 2878 2879 2880 2881 2882 2883 2884 2885
            /* 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;
            }
2886

2887 2888
            for(stream_index = 0; stream_index < stream->nb_streams;
                stream_index++) {
2889
                snprintf(buf, sizeof(buf), "%s/streamid=%d",
2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901
                         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 */
2902
    if (h->session_id[0] == '\0')
2903
        snprintf(h->session_id, sizeof(h->session_id), "%08x%08x",
2904
                 av_lfg_get(&random_state), av_lfg_get(&random_state));
2905 2906 2907 2908

    /* find rtp session, and create it if none found */
    rtp_c = find_rtp_session(h->session_id);
    if (!rtp_c) {
2909
        /* always prefer UDP */
2910
        th = find_transport(h, RTSP_LOWER_TRANSPORT_UDP);
2911
        if (!th) {
2912
            th = find_transport(h, RTSP_LOWER_TRANSPORT_TCP);
2913 2914 2915 2916 2917 2918 2919
            if (!th) {
                rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
                return;
            }
        }

        rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id,
2920
                                   th->lower_transport);
2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931
        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;
        }
    }
2932

2933 2934 2935 2936 2937 2938
    /* 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;
    }
2939

2940 2941 2942 2943 2944 2945 2946 2947
    /* 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);
2948
    if (!th || (th->lower_transport == RTSP_LOWER_TRANSPORT_UDP &&
2949 2950 2951 2952 2953 2954 2955 2956 2957
                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);
2958

2959
    /* setup stream */
2960
    if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) {
2961 2962 2963 2964 2965 2966 2967 2968 2969 2970
        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 */
    url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);

    switch(rtp_c->rtp_protocol) {
2971
    case RTSP_LOWER_TRANSPORT_UDP:
2972 2973 2974 2975 2976 2977
        port = rtp_get_local_port(rtp_c->rtp_handles[stream_index]);
        url_fprintf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
                    "client_port=%d-%d;server_port=%d-%d",
                    th->client_port_min, th->client_port_min + 1,
                    port, port + 1);
        break;
2978
    case RTSP_LOWER_TRANSPORT_TCP:
2979 2980 2981 2982 2983 2984
        url_fprintf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
                    stream_index * 2, stream_index * 2 + 1);
        break;
    default:
        break;
    }
2985
    if (setup.transport_option[0] != '\0')
2986 2987
        url_fprintf(c->pb, ";%s", setup.transport_option);
    url_fprintf(c->pb, "\r\n");
2988

2989 2990 2991 2992 2993 2994 2995

    url_fprintf(c->pb, "\r\n");
}


/* find an rtp connection by using the session ID. Check consistency
   with filename */
2996
static HTTPContext *find_rtp_session_with_url(const char *url,
2997 2998 2999 3000 3001
                                              const char *session_id)
{
    HTTPContext *rtp_c;
    char path1[1024];
    const char *path;
3002 3003
    char buf[1024];
    int s;
3004 3005 3006 3007 3008 3009

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

    /* find which url is asked */
3010
    url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
3011 3012 3013
    path = path1;
    if (*path == '/')
        path++;
3014 3015 3016 3017 3018 3019 3020 3021 3022 3023
    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;
      }
    }
    return NULL;
3024 3025
}

3026
static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPMessageHeader *h)
3027 3028 3029 3030 3031 3032 3033 3034
{
    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;
    }
3035

3036 3037 3038 3039 3040 3041 3042
    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;
    }

3043 3044 3045 3046 3047 3048 3049 3050
#if 0
    /* XXX: seek in stream */
    if (h->range_start != AV_NOPTS_VALUE) {
        printf("range_start=%0.3f\n", (double)h->range_start / AV_TIME_BASE);
        av_seek_frame(rtp_c->fmt_in, -1, h->range_start);
    }
#endif

3051
    rtp_c->state = HTTPSTATE_SEND_DATA;
3052

3053 3054 3055 3056 3057 3058 3059
    /* now everything is OK, so we can send the connection parameters */
    rtsp_reply_header(c, RTSP_STATUS_OK);
    /* session ID */
    url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
    url_fprintf(c->pb, "\r\n");
}

3060
static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPMessageHeader *h)
3061 3062 3063 3064 3065 3066 3067 3068
{
    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;
    }
3069

3070 3071 3072 3073 3074
    if (rtp_c->state != HTTPSTATE_SEND_DATA &&
        rtp_c->state != HTTPSTATE_WAIT_FEED) {
        rtsp_reply_error(c, RTSP_STATUS_STATE);
        return;
    }
3075

3076
    rtp_c->state = HTTPSTATE_READY;
3077
    rtp_c->first_pts = AV_NOPTS_VALUE;
3078 3079 3080 3081 3082 3083 3084
    /* now everything is OK, so we can send the connection parameters */
    rtsp_reply_header(c, RTSP_STATUS_OK);
    /* session ID */
    url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
    url_fprintf(c->pb, "\r\n");
}

3085
static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPMessageHeader *h)
3086 3087
{
    HTTPContext *rtp_c;
Luca Abeni's avatar
Luca Abeni committed
3088
    char session_id[32];
3089 3090 3091 3092 3093 3094

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

3096
    av_strlcpy(session_id, rtp_c->session_id, sizeof(session_id));
Luca Abeni's avatar
Luca Abeni committed
3097

3098 3099 3100 3101 3102 3103
    /* abort the session */
    close_connection(rtp_c);

    /* now everything is OK, so we can send the connection parameters */
    rtsp_reply_header(c, RTSP_STATUS_OK);
    /* session ID */
Luca Abeni's avatar
Luca Abeni committed
3104
    url_fprintf(c->pb, "Session: %s\r\n", session_id);
3105 3106 3107 3108 3109 3110 3111
    url_fprintf(c->pb, "\r\n");
}


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

3112
static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
3113
                                       FFStream *stream, const char *session_id,
3114
                                       enum RTSPLowerTransport rtp_protocol)
3115 3116
{
    HTTPContext *c = NULL;
3117
    const char *proto_str;
3118

3119 3120 3121 3122
    /* XXX: should output a warning page when coming
       close to the connection limit */
    if (nb_connections >= nb_max_connections)
        goto fail;
3123

3124 3125 3126 3127
    /* add a new connection */
    c = av_mallocz(sizeof(HTTPContext));
    if (!c)
        goto fail;
3128

3129 3130
    c->fd = -1;
    c->poll_entry = NULL;
3131
    c->from_addr = *from_addr;
3132 3133 3134 3135 3136 3137
    c->buffer_size = IOBUFFER_INIT_SIZE;
    c->buffer = av_malloc(c->buffer_size);
    if (!c->buffer)
        goto fail;
    nb_connections++;
    c->stream = stream;
3138
    av_strlcpy(c->session_id, session_id, sizeof(c->session_id));
3139 3140
    c->state = HTTPSTATE_READY;
    c->is_packetized = 1;
3141 3142
    c->rtp_protocol = rtp_protocol;

3143
    /* protocol is shown in statistics */
3144
    switch(c->rtp_protocol) {
3145
    case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
3146 3147
        proto_str = "MCAST";
        break;
3148
    case RTSP_LOWER_TRANSPORT_UDP:
3149 3150
        proto_str = "UDP";
        break;
3151
    case RTSP_LOWER_TRANSPORT_TCP:
3152 3153 3154 3155 3156 3157
        proto_str = "TCP";
        break;
    default:
        proto_str = "???";
        break;
    }
3158 3159
    av_strlcpy(c->protocol, "RTP/", sizeof(c->protocol));
    av_strlcat(c->protocol, proto_str, sizeof(c->protocol));
3160

3161 3162
    current_bandwidth += stream->bandwidth;

3163 3164 3165
    c->next = first_http_ctx;
    first_http_ctx = c;
    return c;
3166

3167 3168 3169 3170 3171 3172 3173 3174 3175
 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
3176
   command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
3177
   used. */
3178
static int rtp_new_av_stream(HTTPContext *c,
3179 3180
                             int stream_index, struct sockaddr_in *dest_addr,
                             HTTPContext *rtsp_c)
3181 3182 3183 3184
{
    AVFormatContext *ctx;
    AVStream *st;
    char *ipaddr;
3185
    URLContext *h = NULL;
3186
    uint8_t *dummy_buf;
3187
    int max_packet_size;
3188

3189
    /* now we can open the relevant output stream */
3190
    ctx = avformat_alloc_context();
3191 3192
    if (!ctx)
        return -1;
3193
    ctx->oformat = guess_format("rtp", NULL, NULL);
3194 3195 3196 3197

    st = av_mallocz(sizeof(AVStream));
    if (!st)
        goto fail;
3198
    st->codec= avcodec_alloc_context();
3199 3200 3201
    ctx->nb_streams = 1;
    ctx->streams[0] = st;

3202
    if (!c->stream->feed ||
3203
        c->stream->feed == c->stream)
3204
        memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3205
    else
3206
        memcpy(st,
3207 3208
               c->stream->feed->streams[c->stream->feed_streams[stream_index]],
               sizeof(AVStream));
3209
    st->priv_data = NULL;
3210

3211 3212 3213 3214
    /* build destination RTP address */
    ipaddr = inet_ntoa(dest_addr->sin_addr);

    switch(c->rtp_protocol) {
3215 3216
    case RTSP_LOWER_TRANSPORT_UDP:
    case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
3217
        /* RTP/UDP case */
3218

3219 3220 3221 3222 3223 3224 3225
        /* 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),
3226
                     "rtp://%s:%d?multicast=1&ttl=%d",
3227 3228 3229 3230 3231
                     ipaddr, ntohs(dest_addr->sin_port), ttl);
        } else {
            snprintf(ctx->filename, sizeof(ctx->filename),
                     "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
        }
3232 3233 3234 3235

        if (url_open(&h, ctx->filename, URL_WRONLY) < 0)
            goto fail;
        c->rtp_handles[stream_index] = h;
3236 3237
        max_packet_size = url_get_max_packet_size(h);
        break;
3238
    case RTSP_LOWER_TRANSPORT_TCP:
3239 3240 3241 3242 3243
        /* RTP/TCP case */
        c->rtsp_c = rtsp_c;
        max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
        break;
    default:
3244 3245 3246
        goto fail;
    }

3247
    http_log("%s:%d - - \"PLAY %s/streamid=%d %s\"\n",
3248
             ipaddr, ntohs(dest_addr->sin_port),
3249
             c->stream->filename, stream_index, c->protocol);
3250

3251
    /* normally, no packets should be output here, but the packet size may be checked */
3252
    if (url_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) {
3253 3254 3255
        /* XXX: close stream */
        goto fail;
    }
3256
    av_set_parameters(ctx, NULL);
3257 3258 3259 3260 3261 3262 3263
    if (av_write_header(ctx) < 0) {
    fail:
        if (h)
            url_close(h);
        av_free(ctx);
        return -1;
    }
3264
    url_close_dyn_buf(ctx->pb, &dummy_buf);
3265
    av_free(dummy_buf);
3266

3267 3268 3269 3270 3271 3272 3273
    c->rtp_ctx[stream_index] = ctx;
    return 0;
}

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

3274
static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec)
3275 3276 3277 3278 3279 3280
{
    AVStream *fst;

    fst = av_mallocz(sizeof(AVStream));
    if (!fst)
        return NULL;
3281
    fst->codec= avcodec_alloc_context();
3282
    fst->priv_data = av_mallocz(sizeof(FeedData));
3283
    memcpy(fst->codec, codec, sizeof(AVCodecContext));
3284
    fst->index = stream->nb_streams;
3285
    av_set_pts_info(fst, 33, 1, 90000);
3286 3287 3288 3289
    stream->streams[stream->nb_streams++] = fst;
    return fst;
}

Fabrice Bellard's avatar
Fabrice Bellard committed
3290
/* return the stream number in the feed */
3291
static int add_av_stream(FFStream *feed, AVStream *st)
Fabrice Bellard's avatar
Fabrice Bellard committed
3292 3293 3294 3295 3296
{
    AVStream *fst;
    AVCodecContext *av, *av1;
    int i;

3297
    av = st->codec;
Fabrice Bellard's avatar
Fabrice Bellard committed
3298 3299
    for(i=0;i<feed->nb_streams;i++) {
        st = feed->streams[i];
3300
        av1 = st->codec;
3301 3302
        if (av1->codec_id == av->codec_id &&
            av1->codec_type == av->codec_type &&
Fabrice Bellard's avatar
Fabrice Bellard committed
3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313
            av1->bit_rate == av->bit_rate) {

            switch(av->codec_type) {
            case CODEC_TYPE_AUDIO:
                if (av1->channels == av->channels &&
                    av1->sample_rate == av->sample_rate)
                    goto found;
                break;
            case CODEC_TYPE_VIDEO:
                if (av1->width == av->width &&
                    av1->height == av->height &&
3314 3315
                    av1->time_base.den == av->time_base.den &&
                    av1->time_base.num == av->time_base.num &&
Fabrice Bellard's avatar
Fabrice Bellard committed
3316 3317 3318
                    av1->gop_size == av->gop_size)
                    goto found;
                break;
3319
            default:
3320
                abort();
Fabrice Bellard's avatar
Fabrice Bellard committed
3321 3322 3323
            }
        }
    }
3324

3325
    fst = add_av_stream1(feed, av);
Fabrice Bellard's avatar
Fabrice Bellard committed
3326 3327 3328 3329 3330 3331 3332
    if (!fst)
        return -1;
    return feed->nb_streams - 1;
 found:
    return i;
}

3333
static void remove_stream(FFStream *stream)
3334 3335 3336 3337
{
    FFStream **ps;
    ps = &first_stream;
    while (*ps != NULL) {
3338
        if (*ps == stream)
3339
            *ps = (*ps)->next;
3340
        else
3341 3342 3343 3344
            ps = &(*ps)->next;
    }
}

3345
/* specific mpeg4 handling : we extract the raw parameters */
3346
static void extract_mpeg4_header(AVFormatContext *infile)
3347 3348 3349 3350
{
    int mpeg4_count, i, size;
    AVPacket pkt;
    AVStream *st;
3351
    const uint8_t *p;
3352 3353 3354 3355

    mpeg4_count = 0;
    for(i=0;i<infile->nb_streams;i++) {
        st = infile->streams[i];
3356 3357
        if (st->codec->codec_id == CODEC_ID_MPEG4 &&
            st->codec->extradata_size == 0) {
3358 3359 3360 3361 3362 3363
            mpeg4_count++;
        }
    }
    if (!mpeg4_count)
        return;

3364
    printf("MPEG4 without extra data: trying to find header in %s\n", infile->filename);
3365 3366 3367 3368
    while (mpeg4_count > 0) {
        if (av_read_packet(infile, &pkt) < 0)
            break;
        st = infile->streams[pkt.stream_index];
3369 3370 3371
        if (st->codec->codec_id == CODEC_ID_MPEG4 &&
            st->codec->extradata_size == 0) {
            av_freep(&st->codec->extradata);
3372 3373 3374 3375 3376
            /* 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 */
3377
                if (p[0] == 0x00 && p[1] == 0x00 &&
3378 3379
                    p[2] == 0x01 && p[3] == 0xb6) {
                    size = p - pkt.data;
3380
                    //                    av_hex_dump_log(infile, AV_LOG_DEBUG, pkt.data, size);
3381 3382 3383
                    st->codec->extradata = av_malloc(size);
                    st->codec->extradata_size = size;
                    memcpy(st->codec->extradata, pkt.data, size);
3384 3385 3386 3387 3388 3389 3390 3391 3392 3393
                    break;
                }
                p++;
            }
            mpeg4_count--;
        }
        av_free_packet(&pkt);
    }
}

3394
/* compute the needed AVStream for each file */
3395
static void build_file_streams(void)
3396 3397 3398
{
    FFStream *stream, *stream_next;
    AVFormatContext *infile;
3399
    int i, ret;
3400 3401 3402 3403 3404 3405 3406 3407 3408

    /* gather all streams */
    for(stream = first_stream; stream != NULL; stream = stream_next) {
        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 */
3409
            stream->ap_in = av_mallocz(sizeof(AVFormatParameters));
3410
            if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
3411 3412 3413 3414 3415
                /* specific case : if transport stream output to RTP,
                   we use a raw transport stream reader */
                stream->ap_in->mpeg2ts_raw = 1;
                stream->ap_in->mpeg2ts_compute_pcr = 1;
            }
3416

3417
            http_log("Opening file '%s'\n", stream->feed_filename);
3418 3419
            if ((ret = av_open_input_file(&infile, stream->feed_filename,
                                          stream->ifmt, 0, stream->ap_in)) < 0) {
3420
                http_log("Could not open '%s': %d\n", stream->feed_filename, ret);
3421 3422 3423 3424 3425 3426 3427
                /* 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' */
                if (av_find_stream_info(infile) < 0) {
3428
                    http_log("Could not find codec parameters from '%s'\n",
3429 3430 3431 3432
                             stream->feed_filename);
                    av_close_input_file(infile);
                    goto fail;
                }
3433 3434
                extract_mpeg4_header(infile);

3435
                for(i=0;i<infile->nb_streams;i++)
3436
                    add_av_stream1(stream, infile->streams[i]->codec);
3437

3438 3439 3440 3441 3442 3443
                av_close_input_file(infile);
            }
        }
    }
}

Fabrice Bellard's avatar
Fabrice Bellard committed
3444
/* compute the needed AVStream for each feed */
3445
static void build_feed_streams(void)
Fabrice Bellard's avatar
Fabrice Bellard committed
3446 3447 3448 3449 3450 3451 3452 3453 3454
{
    FFStream *stream, *feed;
    int i;

    /* gather all streams */
    for(stream = first_stream; stream != NULL; stream = stream->next) {
        feed = stream->feed;
        if (feed) {
            if (!stream->is_feed) {
3455
                /* we handle a stream coming from a feed */
3456
                for(i=0;i<stream->nb_streams;i++)
Fabrice Bellard's avatar
Fabrice Bellard committed
3457
                    stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3458 3459 3460 3461 3462 3463 3464 3465 3466
            }
        }
    }

    /* gather all streams */
    for(stream = first_stream; stream != NULL; stream = stream->next) {
        feed = stream->feed;
        if (feed) {
            if (stream->is_feed) {
3467
                for(i=0;i<stream->nb_streams;i++)
Fabrice Bellard's avatar
Fabrice Bellard committed
3468 3469 3470 3471 3472 3473 3474 3475 3476
                    stream->feed_streams[i] = i;
            }
        }
    }

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

3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492
        if (url_exist(feed->feed_filename)) {
            /* See if it matches */
            AVFormatContext *s;
            int matches = 0;

            if (av_open_input_file(&s, feed->feed_filename, NULL, FFM_PACKET_SIZE, NULL) >= 0) {
                /* 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) {
3493
                            http_log("Index & Id do not match for stream %d (%s)\n",
3494
                                   i, feed->feed_filename);
3495 3496 3497 3498
                            matches = 0;
                        } else {
                            AVCodecContext *ccf, *ccs;

3499 3500
                            ccf = sf->codec;
                            ccs = ss->codec;
3501 3502 3503
#define CHECK_CODEC(x)  (ccf->x != ccs->x)

                            if (CHECK_CODEC(codec) || CHECK_CODEC(codec_type)) {
3504
                                http_log("Codecs do not match for stream %d\n", i);
3505 3506
                                matches = 0;
                            } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3507
                                http_log("Codec bitrates do not match for stream %d\n", i);
3508 3509
                                matches = 0;
                            } else if (ccf->codec_type == CODEC_TYPE_VIDEO) {
3510 3511
                                if (CHECK_CODEC(time_base.den) ||
                                    CHECK_CODEC(time_base.num) ||
3512 3513
                                    CHECK_CODEC(width) ||
                                    CHECK_CODEC(height)) {
3514
                                    http_log("Codec width, height and framerate do not match for stream %d\n", i);
3515 3516 3517 3518 3519 3520
                                    matches = 0;
                                }
                            } else if (ccf->codec_type == CODEC_TYPE_AUDIO) {
                                if (CHECK_CODEC(sample_rate) ||
                                    CHECK_CODEC(channels) ||
                                    CHECK_CODEC(frame_size)) {
3521
                                    http_log("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3522 3523 3524
                                    matches = 0;
                                }
                            } else {
3525
                                http_log("Unknown codec type\n");
3526 3527 3528
                                matches = 0;
                            }
                        }
3529
                        if (!matches)
3530 3531
                            break;
                    }
3532
                } else
3533
                    http_log("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3534 3535 3536
                        feed->feed_filename, s->nb_streams, feed->nb_streams);

                av_close_input_file(s);
3537
            } else
3538
                http_log("Deleting feed file '%s' as it appears to be corrupt\n",
3539
                        feed->feed_filename);
3540

3541 3542
            if (!matches) {
                if (feed->readonly) {
3543
                    http_log("Unable to delete feed file '%s' as it is marked readonly\n",
3544 3545 3546
                        feed->feed_filename);
                    exit(1);
                }
3547
                unlink(feed->feed_filename);
3548
            }
3549
        }
Fabrice Bellard's avatar
Fabrice Bellard committed
3550
        if (!url_exist(feed->feed_filename)) {
3551
            AVFormatContext s1 = {0}, *s = &s1;
Fabrice Bellard's avatar
Fabrice Bellard committed
3552

3553
            if (feed->readonly) {
3554
                http_log("Unable to create feed file '%s' as it is marked readonly\n",
3555 3556 3557 3558
                    feed->feed_filename);
                exit(1);
            }

Fabrice Bellard's avatar
Fabrice Bellard committed
3559 3560
            /* only write the header of the ffm file */
            if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
3561 3562
                http_log("Could not open output feed file '%s'\n",
                         feed->feed_filename);
Fabrice Bellard's avatar
Fabrice Bellard committed
3563 3564
                exit(1);
            }
3565
            s->oformat = feed->fmt;
Fabrice Bellard's avatar
Fabrice Bellard committed
3566 3567 3568 3569 3570 3571
            s->nb_streams = feed->nb_streams;
            for(i=0;i<s->nb_streams;i++) {
                AVStream *st;
                st = feed->streams[i];
                s->streams[i] = st;
            }
3572
            av_set_parameters(s, NULL);
3573
            if (av_write_header(s) < 0) {
3574
                http_log("Container doesn't supports the required parameters\n");
3575 3576
                exit(1);
            }
3577 3578
            /* XXX: need better api */
            av_freep(&s->priv_data);
3579
            url_fclose(s->pb);
Fabrice Bellard's avatar
Fabrice Bellard committed
3580 3581 3582 3583
        }
        /* get feed size and write index */
        fd = open(feed->feed_filename, O_RDONLY);
        if (fd < 0) {
3584
            http_log("Could not open output feed file '%s'\n",
Fabrice Bellard's avatar
Fabrice Bellard committed
3585 3586 3587 3588
                    feed->feed_filename);
            exit(1);
        }

3589
        feed->feed_write_index = FFMAX(ffm_read_write_index(fd), FFM_PACKET_SIZE);
Fabrice Bellard's avatar
Fabrice Bellard committed
3590 3591
        feed->feed_size = lseek(fd, 0, SEEK_END);
        /* ensure that we do not wrap before the end of file */
3592
        if (feed->feed_max_size && feed->feed_max_size < feed->feed_size)
Fabrice Bellard's avatar
Fabrice Bellard committed
3593 3594 3595 3596 3597 3598
            feed->feed_max_size = feed->feed_size;

        close(fd);
    }
}

3599 3600 3601
/* compute the bandwidth used by each stream */
static void compute_bandwidth(void)
{
3602 3603
    unsigned bandwidth;
    int i;
3604
    FFStream *stream;
3605

3606 3607 3608 3609
    for(stream = first_stream; stream != NULL; stream = stream->next) {
        bandwidth = 0;
        for(i=0;i<stream->nb_streams;i++) {
            AVStream *st = stream->streams[i];
3610
            switch(st->codec->codec_type) {
3611 3612
            case CODEC_TYPE_AUDIO:
            case CODEC_TYPE_VIDEO:
3613
                bandwidth += st->codec->bit_rate;
3614 3615 3616 3617 3618 3619 3620 3621 3622
                break;
            default:
                break;
            }
        }
        stream->bandwidth = (bandwidth + 999) / 1000;
    }
}

Fabrice Bellard's avatar
Fabrice Bellard committed
3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 3647 3648 3649 3650 3651 3652 3653 3654 3655
static void get_arg(char *buf, int buf_size, const char **pp)
{
    const char *p;
    char *q;
    int quote;

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

/* add a codec and set the default parameters */
3656
static void add_codec(FFStream *stream, AVCodecContext *av)
Fabrice Bellard's avatar
Fabrice Bellard committed
3657 3658 3659 3660 3661 3662 3663 3664 3665 3666 3667 3668 3669 3670 3671 3672
{
    AVStream *st;

    /* compute default parameters */
    switch(av->codec_type) {
    case CODEC_TYPE_AUDIO:
        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;
    case CODEC_TYPE_VIDEO:
        if (av->bit_rate == 0)
            av->bit_rate = 64000;
3673 3674 3675
        if (av->time_base.num == 0){
            av->time_base.den = 5;
            av->time_base.num = 1;
3676
        }
Fabrice Bellard's avatar
Fabrice Bellard committed
3677 3678 3679 3680
        if (av->width == 0 || av->height == 0) {
            av->width = 160;
            av->height = 128;
        }
3681
        /* Bitrate tolerance is less for streaming */
3682
        if (av->bit_rate_tolerance == 0)
3683 3684
            av->bit_rate_tolerance = FFMAX(av->bit_rate / 4,
                      (int64_t)av->bit_rate*av->time_base.num/av->time_base.den);
3685 3686 3687 3688 3689 3690
        if (av->qmin == 0)
            av->qmin = 3;
        if (av->qmax == 0)
            av->qmax = 31;
        if (av->max_qdiff == 0)
            av->max_qdiff = 3;
3691 3692
        av->qcompress = 0.5;
        av->qblur = 0.5;
3693

3694
        if (!av->nsse_weight)
3695 3696 3697 3698 3699 3700
            av->nsse_weight = 8;

        av->frame_skip_cmp = FF_CMP_DCTMAX;
        av->me_method = ME_EPZS;
        av->rc_buffer_aggressivity = 1.0;

3701 3702 3703
        if (!av->rc_eq)
            av->rc_eq = "tex^qComp";
        if (!av->i_quant_factor)
3704
            av->i_quant_factor = -0.8;
3705 3706 3707 3708
        if (!av->b_quant_factor)
            av->b_quant_factor = 1.25;
        if (!av->b_quant_offset)
            av->b_quant_offset = 1.25;
3709 3710
        if (!av->rc_max_rate)
            av->rc_max_rate = av->bit_rate * 2;
3711

3712 3713 3714 3715 3716
        if (av->rc_max_rate && !av->rc_buffer_size) {
            av->rc_buffer_size = av->rc_max_rate;
        }


Fabrice Bellard's avatar
Fabrice Bellard committed
3717
        break;
3718
    default:
3719
        abort();
Fabrice Bellard's avatar
Fabrice Bellard committed
3720 3721 3722 3723 3724
    }

    st = av_mallocz(sizeof(AVStream));
    if (!st)
        return;
3725
    st->codec = avcodec_alloc_context();
Fabrice Bellard's avatar
Fabrice Bellard committed
3726
    stream->streams[stream->nb_streams++] = st;
3727
    memcpy(st->codec, av, sizeof(AVCodecContext));
Fabrice Bellard's avatar
Fabrice Bellard committed
3728 3729
}

3730
static enum CodecID opt_audio_codec(const char *arg)
3731
{
3732
    AVCodec *p= avcodec_find_encoder_by_name(arg);
3733

3734
    if (p == NULL || p->type != CODEC_TYPE_AUDIO)
3735 3736 3737 3738 3739
        return CODEC_ID_NONE;

    return p->id;
}

3740
static enum CodecID opt_video_codec(const char *arg)
3741
{
3742
    AVCodec *p= avcodec_find_encoder_by_name(arg);
3743

3744
    if (p == NULL || p->type != CODEC_TYPE_VIDEO)
3745 3746 3747 3748 3749
        return CODEC_ID_NONE;

    return p->id;
}

3750 3751
/* simplistic plugin support */

3752
#if HAVE_DLOPEN
3753
static void load_module(const char *filename)
3754 3755 3756 3757 3758 3759 3760 3761 3762
{
    void *dll;
    void (*init_func)(void);
    dll = dlopen(filename, RTLD_NOW);
    if (!dll) {
        fprintf(stderr, "Could not load module '%s' - %s\n",
                filename, dlerror());
        return;
    }
3763

3764 3765
    init_func = dlsym(dll, "ffserver_module_init");
    if (!init_func) {
3766
        fprintf(stderr,
3767 3768 3769 3770 3771 3772 3773
                "%s: init function 'ffserver_module_init()' not found\n",
                filename);
        dlclose(dll);
    }

    init_func();
}
3774
#endif
3775

3776
static int ffserver_opt_default(const char *opt, const char *arg,
3777 3778
                       AVCodecContext *avctx, int type)
{
3779
    int ret = 0;
3780 3781
    const AVOption *o = av_find_opt(avctx, opt, NULL, type, type);
    if(o)
3782 3783
        ret = av_set_string3(avctx, opt, arg, 1, NULL);
    return ret;
3784 3785
}

3786
static int parse_ffconfig(const char *filename)
Fabrice Bellard's avatar
Fabrice Bellard committed
3787 3788 3789 3790 3791 3792 3793
{
    FILE *f;
    char line[1024];
    char cmd[64];
    char arg[1024];
    const char *p;
    int val, errors, line_num;
3794
    FFStream **last_stream, *stream, *redirect;
3795
    FFStream **last_feed, *feed, *s;
Fabrice Bellard's avatar
Fabrice Bellard committed
3796
    AVCodecContext audio_enc, video_enc;
3797
    enum CodecID audio_id, video_id;
Fabrice Bellard's avatar
Fabrice Bellard committed
3798 3799 3800 3801 3802 3803

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

Fabrice Bellard's avatar
Fabrice Bellard committed
3805 3806 3807 3808 3809 3810 3811 3812
    errors = 0;
    line_num = 0;
    first_stream = NULL;
    last_stream = &first_stream;
    first_feed = NULL;
    last_feed = &first_feed;
    stream = NULL;
    feed = NULL;
3813
    redirect = NULL;
Fabrice Bellard's avatar
Fabrice Bellard committed
3814 3815 3816 3817 3818 3819 3820
    audio_id = CODEC_ID_NONE;
    video_id = CODEC_ID_NONE;
    for(;;) {
        if (fgets(line, sizeof(line), f) == NULL)
            break;
        line_num++;
        p = line;
3821
        while (isspace(*p))
Fabrice Bellard's avatar
Fabrice Bellard committed
3822 3823 3824 3825 3826
            p++;
        if (*p == '\0' || *p == '#')
            continue;

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

Fabrice Bellard's avatar
Fabrice Bellard committed
3828 3829
        if (!strcasecmp(cmd, "Port")) {
            get_arg(arg, sizeof(arg), &p);
3830 3831 3832 3833 3834 3835 3836
            val = atoi(arg);
            if (val < 1 || val > 65536) {
                fprintf(stderr, "%s:%d: Invalid port: %s\n",
                        filename, line_num, arg);
                errors++;
            }
            my_http_addr.sin_port = htons(val);
Fabrice Bellard's avatar
Fabrice Bellard committed
3837 3838
        } else if (!strcasecmp(cmd, "BindAddress")) {
            get_arg(arg, sizeof(arg), &p);
3839 3840
            if (resolve_host(&my_http_addr.sin_addr, arg) != 0) {
                fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
3841 3842 3843 3844 3845 3846 3847
                        filename, line_num, arg);
                errors++;
            }
        } else if (!strcasecmp(cmd, "NoDaemon")) {
            ffserver_daemon = 0;
        } else if (!strcasecmp(cmd, "RTSPPort")) {
            get_arg(arg, sizeof(arg), &p);
3848 3849 3850 3851 3852 3853 3854
            val = atoi(arg);
            if (val < 1 || val > 65536) {
                fprintf(stderr, "%s:%d: Invalid port: %s\n",
                        filename, line_num, arg);
                errors++;
            }
            my_rtsp_addr.sin_port = htons(atoi(arg));
3855 3856
        } else if (!strcasecmp(cmd, "RTSPBindAddress")) {
            get_arg(arg, sizeof(arg), &p);
3857 3858
            if (resolve_host(&my_rtsp_addr.sin_addr, arg) != 0) {
                fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
Fabrice Bellard's avatar
Fabrice Bellard committed
3859 3860 3861
                        filename, line_num, arg);
                errors++;
            }
3862 3863 3864 3865 3866 3867 3868 3869 3870
        } else if (!strcasecmp(cmd, "MaxHTTPConnections")) {
            get_arg(arg, sizeof(arg), &p);
            val = atoi(arg);
            if (val < 1 || val > 65536) {
                fprintf(stderr, "%s:%d: Invalid MaxHTTPConnections: %s\n",
                        filename, line_num, arg);
                errors++;
            }
            nb_max_http_connections = val;
Fabrice Bellard's avatar
Fabrice Bellard committed
3871 3872 3873
        } else if (!strcasecmp(cmd, "MaxClients")) {
            get_arg(arg, sizeof(arg), &p);
            val = atoi(arg);
3874
            if (val < 1 || val > nb_max_http_connections) {
3875
                fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n",
Fabrice Bellard's avatar
Fabrice Bellard committed
3876 3877 3878 3879 3880
                        filename, line_num, arg);
                errors++;
            } else {
                nb_max_connections = val;
            }
3881
        } else if (!strcasecmp(cmd, "MaxBandwidth")) {
3882
            int64_t llval;
3883
            get_arg(arg, sizeof(arg), &p);
3884 3885
            llval = atoll(arg);
            if (llval < 10 || llval > 10000000) {
3886
                fprintf(stderr, "%s:%d: Invalid MaxBandwidth: %s\n",
3887 3888
                        filename, line_num, arg);
                errors++;
3889
            } else
3890
                max_bandwidth = llval;
Fabrice Bellard's avatar
Fabrice Bellard committed
3891
        } else if (!strcasecmp(cmd, "CustomLog")) {
3892 3893
            if (!ffserver_debug)
                get_arg(logfilename, sizeof(logfilename), &p);
Fabrice Bellard's avatar
Fabrice Bellard committed
3894 3895 3896 3897 3898 3899 3900 3901 3902 3903 3904 3905 3906
        } else if (!strcasecmp(cmd, "<Feed")) {
            /*********************************************/
            /* Feed related options */
            char *q;
            if (stream || feed) {
                fprintf(stderr, "%s:%d: Already in a tag\n",
                        filename, line_num);
            } else {
                feed = av_mallocz(sizeof(FFStream));
                get_arg(feed->filename, sizeof(feed->filename), &p);
                q = strrchr(feed->filename, '>');
                if (*q)
                    *q = '\0';
3907 3908 3909 3910 3911 3912 3913 3914 3915

                for (s = first_feed; s; s = s->next) {
                    if (!strcmp(feed->filename, s->filename)) {
                        fprintf(stderr, "%s:%d: Feed '%s' already registered\n",
                                filename, line_num, s->filename);
                        errors++;
                    }
                }

Fabrice Bellard's avatar
Fabrice Bellard committed
3916 3917 3918 3919 3920 3921 3922
                feed->fmt = guess_format("ffm", NULL, NULL);
                /* 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 :-) */
3923 3924 3925 3926 3927 3928 3929

                /* 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
3930
            }
3931 3932 3933 3934
        } else if (!strcasecmp(cmd, "Launch")) {
            if (feed) {
                int i;

3935
                feed->child_argv = av_mallocz(64 * sizeof(char *));
3936

3937
                for (i = 0; i < 62; i++) {
Alex Beregszaszi's avatar
Alex Beregszaszi committed
3938 3939
                    get_arg(arg, sizeof(arg), &p);
                    if (!arg[0])
3940 3941
                        break;

Alex Beregszaszi's avatar
Alex Beregszaszi committed
3942
                    feed->child_argv[i] = av_strdup(arg);
3943 3944 3945 3946
                }

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

3947 3948 3949 3950 3951
                snprintf(feed->child_argv[i], 30+strlen(feed->filename),
                    "http://%s:%d/%s",
                        (my_http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" :
                    inet_ntoa(my_http_addr.sin_addr),
                    ntohs(my_http_addr.sin_port), feed->filename);
3952
            }
3953 3954 3955 3956 3957 3958 3959
        } else if (!strcasecmp(cmd, "ReadOnlyFile")) {
            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);
            }
Fabrice Bellard's avatar
Fabrice Bellard committed
3960 3961 3962
        } else if (!strcasecmp(cmd, "File")) {
            if (feed) {
                get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3963
            } else if (stream)
Fabrice Bellard's avatar
Fabrice Bellard committed
3964
                get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3965 3966 3967 3968 3969
        } else if (!strcasecmp(cmd, "Truncate")) {
            if (feed) {
                get_arg(arg, sizeof(arg), &p);
                feed->truncate = strtod(arg, NULL);
            }
Fabrice Bellard's avatar
Fabrice Bellard committed
3970 3971
        } else if (!strcasecmp(cmd, "FileMaxSize")) {
            if (feed) {
3972
                char *p1;
Fabrice Bellard's avatar
Fabrice Bellard committed
3973 3974 3975 3976
                double fsize;

                get_arg(arg, sizeof(arg), &p);
                p1 = arg;
3977
                fsize = strtod(p1, &p1);
Fabrice Bellard's avatar
Fabrice Bellard committed
3978 3979 3980 3981 3982 3983 3984 3985 3986 3987 3988
                switch(toupper(*p1)) {
                case 'K':
                    fsize *= 1024;
                    break;
                case 'M':
                    fsize *= 1024 * 1024;
                    break;
                case 'G':
                    fsize *= 1024 * 1024 * 1024;
                    break;
                }
3989
                feed->feed_max_size = (int64_t)fsize;
3990 3991 3992 3993 3994
                if (feed->feed_max_size < FFM_PACKET_SIZE*4) {
                    fprintf(stderr, "%s:%d: Feed max file size is too small, "
                            "must be at least %d\n", filename, line_num, FFM_PACKET_SIZE*4);
                    errors++;
                }
Fabrice Bellard's avatar
Fabrice Bellard committed
3995 3996 3997 3998 3999 4000 4001 4002 4003 4004 4005 4006 4007 4008 4009 4010
            }
        } else if (!strcasecmp(cmd, "</Feed>")) {
            if (!feed) {
                fprintf(stderr, "%s:%d: No corresponding <Feed> for </Feed>\n",
                        filename, line_num);
                errors++;
            }
            feed = NULL;
        } else if (!strcasecmp(cmd, "<Stream")) {
            /*********************************************/
            /* Stream related options */
            char *q;
            if (stream || feed) {
                fprintf(stderr, "%s:%d: Already in a tag\n",
                        filename, line_num);
            } else {
4011
                FFStream *s;
4012
                const AVClass *class;
Fabrice Bellard's avatar
Fabrice Bellard committed
4013 4014 4015 4016 4017
                stream = av_mallocz(sizeof(FFStream));
                get_arg(stream->filename, sizeof(stream->filename), &p);
                q = strrchr(stream->filename, '>');
                if (*q)
                    *q = '\0';
4018 4019 4020 4021 4022 4023 4024 4025 4026

                for (s = first_stream; s; s = s->next) {
                    if (!strcmp(stream->filename, s->filename)) {
                        fprintf(stderr, "%s:%d: Stream '%s' already registered\n",
                                filename, line_num, s->filename);
                        errors++;
                    }
                }

4027
                stream->fmt = guess_stream_format(NULL, stream->filename, NULL);
4028 4029 4030 4031 4032
                /* fetch avclass so AVOption works
                 * FIXME try to use avcodec_get_context_defaults2
                 * without changing defaults too much */
                avcodec_get_context_defaults(&video_enc);
                class = video_enc.av_class;
Fabrice Bellard's avatar
Fabrice Bellard committed
4033 4034
                memset(&audio_enc, 0, sizeof(AVCodecContext));
                memset(&video_enc, 0, sizeof(AVCodecContext));
4035 4036
                audio_enc.av_class = class;
                video_enc.av_class = class;
Fabrice Bellard's avatar
Fabrice Bellard committed
4037 4038 4039 4040 4041 4042
                audio_id = CODEC_ID_NONE;
                video_id = CODEC_ID_NONE;
                if (stream->fmt) {
                    audio_id = stream->fmt->audio_codec;
                    video_id = stream->fmt->video_codec;
                }
4043 4044 4045

                *last_stream = stream;
                last_stream = &stream->next;
Fabrice Bellard's avatar
Fabrice Bellard committed
4046 4047 4048 4049 4050
            }
        } else if (!strcasecmp(cmd, "Feed")) {
            get_arg(arg, sizeof(arg), &p);
            if (stream) {
                FFStream *sfeed;
4051

Fabrice Bellard's avatar
Fabrice Bellard committed
4052 4053 4054 4055 4056 4057
                sfeed = first_feed;
                while (sfeed != NULL) {
                    if (!strcmp(sfeed->filename, arg))
                        break;
                    sfeed = sfeed->next_feed;
                }
4058
                if (!sfeed)
Fabrice Bellard's avatar
Fabrice Bellard committed
4059 4060
                    fprintf(stderr, "%s:%d: feed '%s' not defined\n",
                            filename, line_num, arg);
4061
                else
Fabrice Bellard's avatar
Fabrice Bellard committed
4062 4063 4064 4065
                    stream->feed = sfeed;
            }
        } else if (!strcasecmp(cmd, "Format")) {
            get_arg(arg, sizeof(arg), &p);
4066
            if (stream) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
4067 4068 4069 4070 4071 4072 4073 4074 4075 4076 4077 4078 4079 4080 4081 4082 4083 4084
                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");
                    stream->fmt = guess_stream_format(arg, NULL, NULL);
                    if (!stream->fmt) {
                        fprintf(stderr, "%s:%d: Unknown Format: %s\n",
                                filename, line_num, arg);
                        errors++;
                    }
                }
                if (stream->fmt) {
                    audio_id = stream->fmt->audio_codec;
                    video_id = stream->fmt->video_codec;
Fabrice Bellard's avatar
Fabrice Bellard committed
4085
                }
4086
            }
4087
        } else if (!strcasecmp(cmd, "InputFormat")) {
4088
            get_arg(arg, sizeof(arg), &p);
4089
            if (stream) {
4090 4091 4092 4093 4094
                stream->ifmt = av_find_input_format(arg);
                if (!stream->ifmt) {
                    fprintf(stderr, "%s:%d: Unknown input format: %s\n",
                            filename, line_num, arg);
                }
4095
            }
4096 4097 4098 4099
        } else if (!strcasecmp(cmd, "FaviconURL")) {
            if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
                get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
            } else {
4100
                fprintf(stderr, "%s:%d: FaviconURL only permitted for status streams\n",
4101 4102 4103
                            filename, line_num);
                errors++;
            }
4104
        } else if (!strcasecmp(cmd, "Author")) {
4105
            if (stream)
4106 4107
                get_arg(stream->author, sizeof(stream->author), &p);
        } else if (!strcasecmp(cmd, "Comment")) {
4108
            if (stream)
4109 4110
                get_arg(stream->comment, sizeof(stream->comment), &p);
        } else if (!strcasecmp(cmd, "Copyright")) {
4111
            if (stream)
4112 4113
                get_arg(stream->copyright, sizeof(stream->copyright), &p);
        } else if (!strcasecmp(cmd, "Title")) {
4114
            if (stream)
4115
                get_arg(stream->title, sizeof(stream->title), &p);
4116 4117
        } else if (!strcasecmp(cmd, "Preroll")) {
            get_arg(arg, sizeof(arg), &p);
4118
            if (stream)
4119
                stream->prebuffer = atof(arg) * 1000;
4120
        } else if (!strcasecmp(cmd, "StartSendOnKey")) {
4121
            if (stream)
4122
                stream->send_on_key = 1;
4123 4124 4125 4126
        } else if (!strcasecmp(cmd, "AudioCodec")) {
            get_arg(arg, sizeof(arg), &p);
            audio_id = opt_audio_codec(arg);
            if (audio_id == CODEC_ID_NONE) {
4127
                fprintf(stderr, "%s:%d: Unknown AudioCodec: %s\n",
4128 4129 4130 4131 4132 4133 4134
                        filename, line_num, arg);
                errors++;
            }
        } else if (!strcasecmp(cmd, "VideoCodec")) {
            get_arg(arg, sizeof(arg), &p);
            video_id = opt_video_codec(arg);
            if (video_id == CODEC_ID_NONE) {
4135
                fprintf(stderr, "%s:%d: Unknown VideoCodec: %s\n",
4136 4137 4138
                        filename, line_num, arg);
                errors++;
            }
4139 4140
        } else if (!strcasecmp(cmd, "MaxTime")) {
            get_arg(arg, sizeof(arg), &p);
4141
            if (stream)
4142
                stream->max_time = atof(arg) * 1000;
Fabrice Bellard's avatar
Fabrice Bellard committed
4143 4144
        } else if (!strcasecmp(cmd, "AudioBitRate")) {
            get_arg(arg, sizeof(arg), &p);
4145
            if (stream)
Fabrice Bellard's avatar
Fabrice Bellard committed
4146 4147 4148
                audio_enc.bit_rate = atoi(arg) * 1000;
        } else if (!strcasecmp(cmd, "AudioChannels")) {
            get_arg(arg, sizeof(arg), &p);
4149
            if (stream)
Fabrice Bellard's avatar
Fabrice Bellard committed
4150 4151 4152
                audio_enc.channels = atoi(arg);
        } else if (!strcasecmp(cmd, "AudioSampleRate")) {
            get_arg(arg, sizeof(arg), &p);
4153
            if (stream)
Fabrice Bellard's avatar
Fabrice Bellard committed
4154
                audio_enc.sample_rate = atoi(arg);
4155 4156
        } else if (!strcasecmp(cmd, "AudioQuality")) {
            get_arg(arg, sizeof(arg), &p);
4157
            if (stream) {
Michael Niedermayer's avatar
Michael Niedermayer committed
4158
//                audio_enc.quality = atof(arg) * 1000;
4159
            }
4160 4161 4162 4163 4164 4165 4166 4167 4168 4169
        } else if (!strcasecmp(cmd, "VideoBitRateRange")) {
            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 {
4170
                    fprintf(stderr, "%s:%d: Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n",
4171 4172 4173 4174
                            filename, line_num, arg);
                    errors++;
                }
            }
4175 4176 4177 4178 4179 4180 4181 4182 4183 4184
        } else if (!strcasecmp(cmd, "Debug")) {
            if (stream) {
                get_arg(arg, sizeof(arg), &p);
                video_enc.debug = strtol(arg,0,0);
            }
        } else if (!strcasecmp(cmd, "Strict")) {
            if (stream) {
                get_arg(arg, sizeof(arg), &p);
                video_enc.strict_std_compliance = atoi(arg);
            }
4185 4186 4187
        } else if (!strcasecmp(cmd, "VideoBufferSize")) {
            if (stream) {
                get_arg(arg, sizeof(arg), &p);
4188
                video_enc.rc_buffer_size = atoi(arg) * 8*1024;
4189
            }
4190 4191 4192 4193 4194
        } else if (!strcasecmp(cmd, "VideoBitRateTolerance")) {
            if (stream) {
                get_arg(arg, sizeof(arg), &p);
                video_enc.bit_rate_tolerance = atoi(arg) * 1000;
            }
Fabrice Bellard's avatar
Fabrice Bellard committed
4195 4196 4197 4198 4199 4200 4201 4202
        } else if (!strcasecmp(cmd, "VideoBitRate")) {
            get_arg(arg, sizeof(arg), &p);
            if (stream) {
                video_enc.bit_rate = atoi(arg) * 1000;
            }
        } else if (!strcasecmp(cmd, "VideoSize")) {
            get_arg(arg, sizeof(arg), &p);
            if (stream) {
4203
                av_parse_video_frame_size(&video_enc.width, &video_enc.height, arg);
Fabrice Bellard's avatar
Fabrice Bellard committed
4204 4205 4206 4207 4208 4209 4210 4211 4212 4213
                if ((video_enc.width % 16) != 0 ||
                    (video_enc.height % 16) != 0) {
                    fprintf(stderr, "%s:%d: Image size must be a multiple of 16\n",
                            filename, line_num);
                    errors++;
                }
            }
        } else if (!strcasecmp(cmd, "VideoFrameRate")) {
            get_arg(arg, sizeof(arg), &p);
            if (stream) {
4214 4215 4216 4217 4218 4219 4220 4221
                AVRational frame_rate;
                if (av_parse_video_frame_rate(&frame_rate, arg) < 0) {
                    fprintf(stderr, "Incorrect frame rate\n");
                    errors++;
                } else {
                    video_enc.time_base.num = frame_rate.den;
                    video_enc.time_base.den = frame_rate.num;
                }
Fabrice Bellard's avatar
Fabrice Bellard committed
4222 4223 4224
            }
        } else if (!strcasecmp(cmd, "VideoGopSize")) {
            get_arg(arg, sizeof(arg), &p);
4225
            if (stream)
Fabrice Bellard's avatar
Fabrice Bellard committed
4226 4227
                video_enc.gop_size = atoi(arg);
        } else if (!strcasecmp(cmd, "VideoIntraOnly")) {
4228
            if (stream)
Fabrice Bellard's avatar
Fabrice Bellard committed
4229
                video_enc.gop_size = 1;
4230
        } else if (!strcasecmp(cmd, "VideoHighQuality")) {
4231
            if (stream)
4232
                video_enc.mb_decision = FF_MB_DECISION_BITS;
4233 4234
        } else if (!strcasecmp(cmd, "Video4MotionVector")) {
            if (stream) {
4235
                video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove
4236 4237
                video_enc.flags |= CODEC_FLAG_4MV;
            }
4238 4239 4240 4241 4242 4243 4244 4245 4246 4247 4248 4249 4250 4251
        } else if (!strcasecmp(cmd, "AVOptionVideo") ||
                   !strcasecmp(cmd, "AVOptionAudio")) {
            char arg2[1024];
            AVCodecContext *avctx;
            int type;
            get_arg(arg, sizeof(arg), &p);
            get_arg(arg2, sizeof(arg2), &p);
            if (!strcasecmp(cmd, "AVOptionVideo")) {
                avctx = &video_enc;
                type = AV_OPT_FLAG_VIDEO_PARAM;
            } else {
                avctx = &audio_enc;
                type = AV_OPT_FLAG_AUDIO_PARAM;
            }
4252
            if (ffserver_opt_default(arg, arg2, avctx, type|AV_OPT_FLAG_ENCODING_PARAM)) {
4253 4254 4255
                fprintf(stderr, "AVOption error: %s %s\n", arg, arg2);
                errors++;
            }
4256 4257
        } else if (!strcasecmp(cmd, "VideoTag")) {
            get_arg(arg, sizeof(arg), &p);
4258
            if ((strlen(arg) == 4) && stream)
4259
                video_enc.codec_tag = AV_RL32(arg);
4260
        } else if (!strcasecmp(cmd, "BitExact")) {
4261
            if (stream)
4262 4263
                video_enc.flags |= CODEC_FLAG_BITEXACT;
        } else if (!strcasecmp(cmd, "DctFastint")) {
4264
            if (stream)
4265 4266
                video_enc.dct_algo  = FF_DCT_FASTINT;
        } else if (!strcasecmp(cmd, "IdctSimple")) {
4267
            if (stream)
4268 4269 4270 4271 4272 4273 4274
                video_enc.idct_algo = FF_IDCT_SIMPLE;
        } else if (!strcasecmp(cmd, "Qscale")) {
            get_arg(arg, sizeof(arg), &p);
            if (stream) {
                video_enc.flags |= CODEC_FLAG_QSCALE;
                video_enc.global_quality = FF_QP2LAMBDA * atoi(arg);
            }
4275
        } else if (!strcasecmp(cmd, "VideoQDiff")) {
4276
            get_arg(arg, sizeof(arg), &p);
4277 4278 4279 4280 4281 4282 4283 4284 4285
            if (stream) {
                video_enc.max_qdiff = atoi(arg);
                if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
                    fprintf(stderr, "%s:%d: VideoQDiff out of range\n",
                            filename, line_num);
                    errors++;
                }
            }
        } else if (!strcasecmp(cmd, "VideoQMax")) {
4286
            get_arg(arg, sizeof(arg), &p);
4287 4288 4289 4290 4291 4292 4293 4294 4295
            if (stream) {
                video_enc.qmax = atoi(arg);
                if (video_enc.qmax < 1 || video_enc.qmax > 31) {
                    fprintf(stderr, "%s:%d: VideoQMax out of range\n",
                            filename, line_num);
                    errors++;
                }
            }
        } else if (!strcasecmp(cmd, "VideoQMin")) {
4296
            get_arg(arg, sizeof(arg), &p);
4297 4298 4299 4300 4301 4302 4303 4304
            if (stream) {
                video_enc.qmin = atoi(arg);
                if (video_enc.qmin < 1 || video_enc.qmin > 31) {
                    fprintf(stderr, "%s:%d: VideoQMin out of range\n",
                            filename, line_num);
                    errors++;
                }
            }
4305 4306
        } else if (!strcasecmp(cmd, "LumaElim")) {
            get_arg(arg, sizeof(arg), &p);
4307
            if (stream)
4308 4309 4310
                video_enc.luma_elim_threshold = atoi(arg);
        } else if (!strcasecmp(cmd, "ChromaElim")) {
            get_arg(arg, sizeof(arg), &p);
4311
            if (stream)
4312 4313 4314
                video_enc.chroma_elim_threshold = atoi(arg);
        } else if (!strcasecmp(cmd, "LumiMask")) {
            get_arg(arg, sizeof(arg), &p);
4315
            if (stream)
4316 4317 4318
                video_enc.lumi_masking = atof(arg);
        } else if (!strcasecmp(cmd, "DarkMask")) {
            get_arg(arg, sizeof(arg), &p);
4319
            if (stream)
4320
                video_enc.dark_masking = atof(arg);
Fabrice Bellard's avatar
Fabrice Bellard committed
4321 4322 4323 4324
        } else if (!strcasecmp(cmd, "NoVideo")) {
            video_id = CODEC_ID_NONE;
        } else if (!strcasecmp(cmd, "NoAudio")) {
            audio_id = CODEC_ID_NONE;
4325 4326 4327 4328
        } else if (!strcasecmp(cmd, "ACL")) {
            IPAddressACL acl;

            get_arg(arg, sizeof(arg), &p);
4329
            if (strcasecmp(arg, "allow") == 0)
4330
                acl.action = IP_ALLOW;
4331
            else if (strcasecmp(arg, "deny") == 0)
4332
                acl.action = IP_DENY;
4333
            else {
4334 4335 4336 4337 4338 4339
                fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
                        filename, line_num, arg);
                errors++;
            }

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

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

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

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

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

Stanislav Brabec's avatar
Stanislav Brabec committed
4362
                acl.next = 0;
4363 4364
                *nacl = acl;

4365
                if (stream)
4366
                    naclp = &stream->acl;
4367
                else if (feed)
4368
                    naclp = &feed->acl;
4369
                else {
4370 4371 4372 4373 4374 4375 4376 4377 4378 4379 4380 4381
                    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;
                }
            }
4382 4383 4384 4385
        } else if (!strcasecmp(cmd, "RTSPOption")) {
            get_arg(arg, sizeof(arg), &p);
            if (stream) {
                av_freep(&stream->rtsp_option);
Alex Beregszaszi's avatar
Alex Beregszaszi committed
4386
                stream->rtsp_option = av_strdup(arg);
4387
            }
4388 4389 4390
        } else if (!strcasecmp(cmd, "MulticastAddress")) {
            get_arg(arg, sizeof(arg), &p);
            if (stream) {
4391 4392
                if (resolve_host(&stream->multicast_ip, arg) != 0) {
                    fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
4393 4394 4395 4396
                            filename, line_num, arg);
                    errors++;
                }
                stream->is_multicast = 1;
4397
                stream->loop = 1; /* default is looping */
4398 4399 4400
            }
        } else if (!strcasecmp(cmd, "MulticastPort")) {
            get_arg(arg, sizeof(arg), &p);
4401
            if (stream)
4402
                stream->multicast_port = atoi(arg);
4403 4404
        } else if (!strcasecmp(cmd, "MulticastTTL")) {
            get_arg(arg, sizeof(arg), &p);
4405
            if (stream)
4406 4407
                stream->multicast_ttl = atoi(arg);
        } else if (!strcasecmp(cmd, "NoLoop")) {
4408
            if (stream)
4409
                stream->loop = 0;
Fabrice Bellard's avatar
Fabrice Bellard committed
4410 4411 4412 4413 4414
        } else if (!strcasecmp(cmd, "</Stream>")) {
            if (!stream) {
                fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n",
                        filename, line_num);
                errors++;
4415
            } else {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
4416 4417 4418 4419 4420 4421 4422 4423 4424 4425 4426
                if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
                    if (audio_id != CODEC_ID_NONE) {
                        audio_enc.codec_type = CODEC_TYPE_AUDIO;
                        audio_enc.codec_id = audio_id;
                        add_codec(stream, &audio_enc);
                    }
                    if (video_id != CODEC_ID_NONE) {
                        video_enc.codec_type = CODEC_TYPE_VIDEO;
                        video_enc.codec_id = video_id;
                        add_codec(stream, &video_enc);
                    }
Fabrice Bellard's avatar
Fabrice Bellard committed
4427
                }
Baptiste Coudurier's avatar
Baptiste Coudurier committed
4428
                stream = NULL;
4429
            }
4430 4431 4432 4433 4434 4435 4436 4437 4438 4439 4440 4441 4442 4443 4444 4445 4446 4447 4448
        } else if (!strcasecmp(cmd, "<Redirect")) {
            /*********************************************/
            char *q;
            if (stream || feed || redirect) {
                fprintf(stderr, "%s:%d: Already in a tag\n",
                        filename, line_num);
                errors++;
            } 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;
            }
        } else if (!strcasecmp(cmd, "URL")) {
4449
            if (redirect)
4450 4451 4452 4453 4454 4455
                get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
        } else if (!strcasecmp(cmd, "</Redirect>")) {
            if (!redirect) {
                fprintf(stderr, "%s:%d: No corresponding <Redirect> for </Redirect>\n",
                        filename, line_num);
                errors++;
4456
            } else {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
4457 4458 4459 4460 4461 4462
                if (!redirect->feed_filename[0]) {
                    fprintf(stderr, "%s:%d: No URL found for <Redirect>\n",
                            filename, line_num);
                    errors++;
                }
                redirect = NULL;
4463
            }
4464 4465
        } else if (!strcasecmp(cmd, "LoadModule")) {
            get_arg(arg, sizeof(arg), &p);
4466
#if HAVE_DLOPEN
4467
            load_module(arg);
4468
#else
4469
            fprintf(stderr, "%s:%d: Module support not compiled into this version: '%s'\n",
4470 4471 4472
                    filename, line_num, arg);
            errors++;
#endif
Fabrice Bellard's avatar
Fabrice Bellard committed
4473
        } else {
4474
            fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n",
Fabrice Bellard's avatar
Fabrice Bellard committed
4475 4476 4477 4478 4479 4480 4481 4482 4483 4484 4485
                    filename, line_num, cmd);
        }
    }

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

4486 4487 4488 4489 4490 4491 4492 4493 4494 4495 4496 4497 4498 4499 4500
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);

4501
                if (uptime < 30)
4502 4503 4504 4505 4506 4507 4508 4509 4510
                    /* Turn off any more restarts */
                    feed->child_argv = 0;
            }
        }
    }

    need_to_start_children = 1;
}

4511
static void opt_debug(void)
4512 4513 4514
{
    ffserver_debug = 1;
    ffserver_daemon = 0;
4515
    logfilename[0] = '-';
4516 4517 4518 4519
}

static void opt_show_help(void)
{
4520
    printf("usage: ffserver [options]\n"
4521 4522 4523 4524 4525 4526
           "Hyper fast multi format Audio/Video streaming server\n");
    printf("\n");
    show_help_options(options, "Main options:\n", 0, 0);
}

static const OptionDef options[] = {
4527
    { "h", OPT_EXIT, {(void*)opt_show_help}, "show help" },
4528
    { "version", OPT_EXIT, {(void*)show_version}, "show version" },
4529
    { "L", OPT_EXIT, {(void*)show_license}, "show license" },
4530
    { "formats", OPT_EXIT, {(void*)show_formats}, "show available formats, codecs, protocols, ..." },
4531 4532 4533 4534 4535 4536
    { "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
4537 4538
int main(int argc, char **argv)
{
4539
    struct sigaction sigact;
Fabrice Bellard's avatar
Fabrice Bellard committed
4540

4541
    av_register_all();
Fabrice Bellard's avatar
Fabrice Bellard committed
4542

4543
    show_banner();
4544

Fabrice Bellard's avatar
Fabrice Bellard committed
4545 4546
    config_filename = "/etc/ffserver.conf";

4547
    my_program_name = argv[0];
4548
    my_program_dir = getcwd(0, 0);
4549
    ffserver_daemon = 1;
4550

4551
    parse_options(argc, argv, options, NULL);
Fabrice Bellard's avatar
Fabrice Bellard committed
4552

4553
    unsetenv("http_proxy");             /* Kill the http_proxy */
4554

4555
    av_lfg_init(&random_state, ff_random_get_seed());
4556

4557 4558 4559 4560 4561
    memset(&sigact, 0, sizeof(sigact));
    sigact.sa_handler = handle_child_exit;
    sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
    sigaction(SIGCHLD, &sigact, 0);

Fabrice Bellard's avatar
Fabrice Bellard committed
4562 4563 4564 4565 4566
    if (parse_ffconfig(config_filename) < 0) {
        fprintf(stderr, "Incorrect config file - exiting.\n");
        exit(1);
    }

4567 4568 4569
    /* open log file if needed */
    if (logfilename[0] != '\0') {
        if (!strcmp(logfilename, "-"))
4570
            logfile = stdout;
4571 4572 4573 4574 4575
        else
            logfile = fopen(logfilename, "a");
        av_log_set_callback(http_av_log);
    }

4576 4577
    build_file_streams();

Fabrice Bellard's avatar
Fabrice Bellard committed
4578 4579
    build_feed_streams();

4580 4581
    compute_bandwidth();

4582 4583 4584 4585 4586 4587 4588 4589 4590 4591 4592 4593 4594 4595 4596 4597
    /* put the process in background and detach it from its TTY */
    if (ffserver_daemon) {
        int pid;

        pid = fork();
        if (pid < 0) {
            perror("fork");
            exit(1);
        } else if (pid > 0) {
            /* parent : exit */
            exit(0);
        } else {
            /* child */
            setsid();
            close(0);
            open("/dev/null", O_RDWR);
4598
            if (strcmp(logfilename, "-") != 0) {
4599 4600 4601 4602
                close(1);
                dup(0);
            }
            close(2);
4603 4604 4605 4606
            dup(0);
        }
    }

Fabrice Bellard's avatar
Fabrice Bellard committed
4607 4608 4609
    /* signal init */
    signal(SIGPIPE, SIG_IGN);

4610 4611 4612
    if (ffserver_daemon)
        chdir("/");

4613
    if (http_server() < 0) {
4614
        http_log("Could not start server\n");
Fabrice Bellard's avatar
Fabrice Bellard committed
4615 4616 4617 4618 4619
        exit(1);
    }

    return 0;
}