4026 lines · 125.3 KB · C
RawDownload
/* * Copyright (c) 2000, 2001, 2002 Fabrice Bellard * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /** * @file * multiple format streaming server based on the FFmpeg libraries */ #include "config.h"#if !HAVE_CLOSESOCKET#define closesocket close#endif#include <string.h>#include <stdlib.h>#include <stdio.h>#include "libavformat/avformat.h"/* FIXME: those are internal headers, ffserver _really_ shouldn't use them */#include "libavformat/rtpproto.h"#include "libavformat/rtsp.h"#include "libavformat/avio_internal.h"#include "libavformat/internal.h" #include "libavutil/avassert.h"#include "libavutil/avstring.h"#include "libavutil/lfg.h"#include "libavutil/dict.h"#include "libavutil/intreadwrite.h"#include "libavutil/mathematics.h"#include "libavutil/random_seed.h"#include "libavutil/rational.h"#include "libavutil/parseutils.h"#include "libavutil/opt.h"#include "libavutil/time.h" #include <stdarg.h>#if HAVE_UNISTD_H#include <unistd.h>#endif#include <fcntl.h>#include <sys/ioctl.h>#if HAVE_POLL_H#include <poll.h>#endif#include <errno.h>#include <time.h>#include <sys/wait.h>#include <signal.h> #include "cmdutils.h"#include "ffserver_config.h" #define PATH_LENGTH 1024 const char program_name[] = "ffserver";const int program_birth_year = 2000; static const OptionDef options[]; enum HTTPState {    HTTPSTATE_WAIT_REQUEST,    HTTPSTATE_SEND_HEADER,    HTTPSTATE_SEND_DATA_HEADER,    HTTPSTATE_SEND_DATA,          /* sending TCP or UDP data */    HTTPSTATE_SEND_DATA_TRAILER,    HTTPSTATE_RECEIVE_DATA,    HTTPSTATE_WAIT_FEED,          /* wait for data from the feed */    HTTPSTATE_READY,     RTSPSTATE_WAIT_REQUEST,    RTSPSTATE_SEND_REPLY,    RTSPSTATE_SEND_PACKET,}; static const char * const http_state[] = {    "HTTP_WAIT_REQUEST",    "HTTP_SEND_HEADER",     "SEND_DATA_HEADER",    "SEND_DATA",    "SEND_DATA_TRAILER",    "RECEIVE_DATA",    "WAIT_FEED",    "READY",     "RTSP_WAIT_REQUEST",    "RTSP_SEND_REPLY",    "RTSP_SEND_PACKET",}; #define IOBUFFER_INIT_SIZE 8192 /* timeouts are in ms */#define HTTP_REQUEST_TIMEOUT (15 * 1000)#define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000) #define SYNC_TIMEOUT (10 * 1000) typedef struct RTSPActionServerSetup {    uint32_t ipaddr;    char transport_option[512];} RTSPActionServerSetup; typedef struct {    int64_t count1, count2;    int64_t time1, time2;} DataRateData; /* 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 */    int64_t timeout;    uint8_t *buffer_ptr, *buffer_end;    int http_error;    int post;    int chunked_encoding;    int chunk_size;               /* 0 if it needs to be read */    struct HTTPContext *next;    int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */    int64_t data_count;    /* feed input */    int feed_fd;    /* input format handling */    AVFormatContext *fmt_in;    int64_t start_time;            /* In milliseconds - this wraps fairly often */    int64_t first_pts;            /* initial pts value */    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 */    /* output format handling */    struct FFServerStream *stream;    /* -1 is invalid stream */    int feed_streams[FFSERVER_MAX_STREAMS]; /* index of streams in the feed */    int switch_feed_streams[FFSERVER_MAX_STREAMS]; /* index of streams in the feed */    int switch_pending;    AVFormatContext *pfmt_ctx; /* instance of FFServerStream for one user */    int last_packet_sent; /* true if last data packet was sent */    int suppress_log;    DataRateData datarate;    int wmp_client_id;    char protocol[16];    char method[16];    char url[128];    char clean_url[128*7];    int buffer_size;    uint8_t *buffer;    int is_packetized; /* if true, the stream is packetized */    int packet_stream_index; /* current stream for output in state machine */     /* RTSP state specific */    uint8_t *pb_buffer; /* XXX: use that in all the code */    AVIOContext *pb;    int seq; /* RTSP sequence number */     /* RTP state specific */    enum RTSPLowerTransport rtp_protocol;    char session_id[32]; /* session id */    AVFormatContext *rtp_ctx[FFSERVER_MAX_STREAMS];     /* RTP/UDP specific */    URLContext *rtp_handles[FFSERVER_MAX_STREAMS];     /* RTP/TCP specific */    struct HTTPContext *rtsp_c;    uint8_t *packet_buffer, *packet_buffer_ptr, *packet_buffer_end;} HTTPContext; static HTTPContext *first_http_ctx; static FFServerConfig config = {    .nb_max_http_connections = 2000,    .nb_max_connections = 5,    .max_bandwidth = 1000,    .use_defaults = 1,}; static void new_connection(int server_fd, int is_rtsp);static void close_connection(HTTPContext *c); /* HTTP handling */static int handle_connection(HTTPContext *c);static inline void print_stream_params(AVIOContext *pb, FFServerStream *stream);static void compute_status(HTTPContext *c);static int open_input_stream(HTTPContext *c, const char *info);static int http_parse_request(HTTPContext *c);static int http_send_data(HTTPContext *c);static int http_start_receive_data(HTTPContext *c);static int http_receive_data(HTTPContext *c); /* RTSP handling */static int rtsp_parse_request(HTTPContext *c);static void rtsp_cmd_describe(HTTPContext *c, const char *url);static void rtsp_cmd_options(HTTPContext *c, const char *url);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_interrupt(HTTPContext *c, const char *url,                               RTSPMessageHeader *h, int pause_only); /* SDP handling */static int prepare_sdp_description(FFServerStream *stream, uint8_t **pbuffer,                                   struct in_addr my_ip); /* RTP handling */static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,                                       FFServerStream *stream,                                       const char *session_id,                                       enum RTSPLowerTransport rtp_protocol);static int rtp_new_av_stream(HTTPContext *c,                             int stream_index, struct sockaddr_in *dest_addr,                             HTTPContext *rtsp_c);/* utils */static size_t htmlencode (const char *src, char **dest);static inline void cp_html_entity (char *buffer, const char *entity);static inline int check_codec_match(LayeredAVStream *ccf, AVStream *ccs, int stream); static const char *my_program_name; static int no_launch;static int need_to_start_children; /* maximum number of simultaneous HTTP connections */static unsigned int nb_connections; static uint64_t current_bandwidth; /* Making this global saves on passing it around everywhere */static int64_t cur_time; static AVLFG random_state; static FILE *logfile = NULL; static void unlayer_stream(AVStream *st, LayeredAVStream *lst){    avcodec_free_context(&st->codec);    avcodec_parameters_free(&st->codecpar);#define COPY(a) st->a = lst->a;    COPY(index)    COPY(id)    COPY(codec)    COPY(codecpar)    COPY(time_base)    COPY(pts_wrap_bits)    COPY(sample_aspect_ratio)    COPY(recommended_encoder_configuration)} static inline void cp_html_entity (char *buffer, const char *entity) {    if (!buffer || !entity)        return;    while (*entity)        *buffer++ = *entity++;} /** * Substitutes known conflicting chars on a text string with * their corresponding HTML entities. * * Returns the number of bytes in the 'encoded' representation * not including the terminating NUL. */static size_t htmlencode (const char *src, char **dest) {    const char *amp = "&amp;";    const char *lt  = "&lt;";    const char *gt  = "&gt;";    const char *start;    char *tmp;    size_t final_size = 0;     if (!src)        return 0;     start = src;     /* Compute needed dest size */    while (*src != '\0') {        switch(*src) {            case 38: /* & */                final_size += 5;                break;            case 60: /* < */            case 62: /* > */                final_size += 4;                break;            default:                final_size++;        }        src++;    }     src = start;    *dest = av_mallocz(final_size + 1);    if (!*dest)        return 0;     /* Build dest */    tmp = *dest;    while (*src != '\0') {        switch(*src) {            case 38: /* & */                cp_html_entity (tmp, amp);                tmp += 5;                break;            case 60: /* < */                cp_html_entity (tmp, lt);                tmp += 4;                break;            case 62: /* > */                cp_html_entity (tmp, gt);                tmp += 4;                break;            default:                *tmp = *src;                tmp += 1;        }        src++;    }    *tmp = '\0';     return final_size;} static int64_t ffm_read_write_index(int fd){    uint8_t buf[8];     if (lseek(fd, 8, SEEK_SET) < 0)        return AVERROR(EIO);    if (read(fd, buf, 8) != 8)        return AVERROR(EIO);    return AV_RB64(buf);} static int ffm_write_write_index(int fd, int64_t pos){    uint8_t buf[8];    int i;     for(i=0;i<8;i++)        buf[i] = (pos >> (56 - i * 8)) & 0xff;    if (lseek(fd, 8, SEEK_SET) < 0)        goto bail_eio;    if (write(fd, buf, 8) != 8)        goto bail_eio;     return 8; bail_eio:    return AVERROR(EIO);} static void ffm_set_write_index(AVFormatContext *s, int64_t pos,                                int64_t file_size){    av_opt_set_int(s, "server_attached", 1, AV_OPT_SEARCH_CHILDREN);    av_opt_set_int(s, "ffm_write_index", pos, AV_OPT_SEARCH_CHILDREN);    av_opt_set_int(s, "ffm_file_size", file_size, AV_OPT_SEARCH_CHILDREN);} static char *ctime1(char *buf2, size_t buf_size){    time_t ti;    char *p;     ti = time(NULL);    p = ctime(&ti);    if (!p || !*p) {        *buf2 = '\0';        return buf2;    }    av_strlcpy(buf2, p, buf_size);    p = buf2 + strlen(buf2) - 1;    if (*p == '\n')        *p = '\0';    return buf2;} static void http_vlog(const char *fmt, va_list vargs){    static int print_prefix = 1;    char buf[32];     if (!logfile)        return;     if (print_prefix) {        ctime1(buf, sizeof(buf));        fprintf(logfile, "%s ", buf);    }    print_prefix = strstr(fmt, "\n") != NULL;    vfprintf(logfile, fmt, vargs);    fflush(logfile);} #ifdef __GNUC____attribute__ ((format (printf, 1, 2)))#endifstatic void 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;    if (level > av_log_get_level())        return;    if (print_prefix && avc)        http_log("[%s @ %p]", avc->item_name(ptr), ptr);    print_prefix = strstr(fmt, "\n") != NULL;    http_vlog(fmt, vargs);} static void log_connection(HTTPContext *c){    if (c->suppress_log)        return;     http_log("%s - - [%s] \"%s %s\" %d %"PRId64"\n",             inet_ntoa(c->from_addr.sin_addr), c->method, c->url,             c->protocol, (c->http_error ? c->http_error : 200), c->data_count);} static void update_datarate(DataRateData *drd, int64_t count){    if (!drd->time1 && !drd->count1) {        drd->time1 = drd->time2 = cur_time;        drd->count1 = drd->count2 = count;    } else if (cur_time - drd->time2 > 5000) {        drd->time1 = drd->time2;        drd->count1 = drd->count2;        drd->time2 = cur_time;        drd->count2 = count;    }} /* In bytes per second */static int compute_datarate(DataRateData *drd, int64_t count){    if (cur_time == drd->time1)        return 0;     return ((count - drd->count1) * 1000) / (cur_time - drd->time1);}  static void start_children(FFServerStream *feed){    char *pathname;    char *slash;    int i;    size_t cmd_length;     if (no_launch)        return;     cmd_length = strlen(my_program_name);    /**    * FIXME: WIP Safeguard. Remove after clearing all harcoded    * '1024' path lengths    */    if (cmd_length > PATH_LENGTH - 1) {        http_log("Could not start children. Command line: '%s' exceeds "                    "path length limit (%d)\n", my_program_name, PATH_LENGTH);        return;    }     slash = strrchr(my_program_name, '/');    if (!slash) {        pathname = av_mallocz(sizeof("ffmpeg"));

Lines 1–500 of 4026.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t