Skip to content
Snippets Groups Projects
ffserver.c 148 KiB
Newer Older
Fabrice Bellard's avatar
Fabrice Bellard committed
                audio_enc.bit_rate = atoi(arg) * 1000;
        } else if (!strcasecmp(cmd, "AudioChannels")) {
            get_arg(arg, sizeof(arg), &p);
Fabrice Bellard's avatar
Fabrice Bellard committed
                audio_enc.channels = atoi(arg);
        } else if (!strcasecmp(cmd, "AudioSampleRate")) {
            get_arg(arg, sizeof(arg), &p);
Fabrice Bellard's avatar
Fabrice Bellard committed
                audio_enc.sample_rate = atoi(arg);
        } else if (!strcasecmp(cmd, "AudioQuality")) {
            get_arg(arg, sizeof(arg), &p);
Michael Niedermayer's avatar
Michael Niedermayer committed
//                audio_enc.quality = atof(arg) * 1000;
        } 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 {
                    fprintf(stderr, "%s:%d: Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n",
        } 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);
            }
        } else if (!strcasecmp(cmd, "VideoBufferSize")) {
            if (stream) {
                get_arg(arg, sizeof(arg), &p);
                video_enc.rc_buffer_size = atoi(arg) * 8*1024;
        } 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
        } 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) {
                av_parse_video_frame_size(&video_enc.width, &video_enc.height, arg);
Fabrice Bellard's avatar
Fabrice Bellard committed
                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) {
                video_enc.time_base.num= DEFAULT_FRAME_RATE_BASE;
                video_enc.time_base.den = (int)(strtod(arg, NULL) * video_enc.time_base.num);
Fabrice Bellard's avatar
Fabrice Bellard committed
            }
        } else if (!strcasecmp(cmd, "VideoGopSize")) {
            get_arg(arg, sizeof(arg), &p);
Fabrice Bellard's avatar
Fabrice Bellard committed
                video_enc.gop_size = atoi(arg);
        } else if (!strcasecmp(cmd, "VideoIntraOnly")) {
Fabrice Bellard's avatar
Fabrice Bellard committed
                video_enc.gop_size = 1;
        } else if (!strcasecmp(cmd, "VideoHighQuality")) {
                video_enc.mb_decision = FF_MB_DECISION_BITS;
        } else if (!strcasecmp(cmd, "Video4MotionVector")) {
            if (stream) {
                video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove
        } else if (!strcasecmp(cmd, "VideoTag")) {
            get_arg(arg, sizeof(arg), &p);
            if ((strlen(arg) == 4) && stream)
                video_enc.codec_tag = ff_get_fourcc(arg);
        } else if (!strcasecmp(cmd, "BitExact")) {
                video_enc.flags |= CODEC_FLAG_BITEXACT;
        } else if (!strcasecmp(cmd, "DctFastint")) {
                video_enc.dct_algo  = FF_DCT_FASTINT;
        } else if (!strcasecmp(cmd, "IdctSimple")) {
                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);
            }
        } else if (!strcasecmp(cmd, "VideoQDiff")) {
            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")) {
            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")) {
            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++;
                }
            }
        } else if (!strcasecmp(cmd, "LumaElim")) {
            get_arg(arg, sizeof(arg), &p);
                video_enc.luma_elim_threshold = atoi(arg);
        } else if (!strcasecmp(cmd, "ChromaElim")) {
            get_arg(arg, sizeof(arg), &p);
                video_enc.chroma_elim_threshold = atoi(arg);
        } else if (!strcasecmp(cmd, "LumiMask")) {
            get_arg(arg, sizeof(arg), &p);
                video_enc.lumi_masking = atof(arg);
        } else if (!strcasecmp(cmd, "DarkMask")) {
            get_arg(arg, sizeof(arg), &p);
Fabrice Bellard's avatar
Fabrice Bellard committed
        } else if (!strcasecmp(cmd, "NoVideo")) {
            video_id = CODEC_ID_NONE;
        } else if (!strcasecmp(cmd, "NoAudio")) {
            audio_id = CODEC_ID_NONE;
        } else if (!strcasecmp(cmd, "ACL")) {
            IPAddressACL acl;

            get_arg(arg, sizeof(arg), &p);
            if (strcasecmp(arg, "allow") == 0)
            else if (strcasecmp(arg, "deny") == 0)
                fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
                        filename, line_num, arg);
                errors++;
            }

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

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

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

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

Stanislav Brabec's avatar
Stanislav Brabec committed
                acl.next = 0;
                    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;
                }
            }
        } 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
                stream->rtsp_option = av_strdup(arg);
        } else if (!strcasecmp(cmd, "MulticastAddress")) {
            get_arg(arg, sizeof(arg), &p);
            if (stream) {
                if (resolve_host(&stream->multicast_ip, arg) != 0) {
                    fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
                            filename, line_num, arg);
                    errors++;
                }
                stream->is_multicast = 1;
                stream->loop = 1; /* default is looping */
            }
        } else if (!strcasecmp(cmd, "MulticastPort")) {
            get_arg(arg, sizeof(arg), &p);
        } else if (!strcasecmp(cmd, "MulticastTTL")) {
            get_arg(arg, sizeof(arg), &p);
                stream->multicast_ttl = atoi(arg);
        } else if (!strcasecmp(cmd, "NoLoop")) {
Fabrice Bellard's avatar
Fabrice Bellard committed
        } else if (!strcasecmp(cmd, "</Stream>")) {
            if (!stream) {
                fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n",
                        filename, line_num);
                errors++;
            }
            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);
                }
            }
            stream = NULL;
        } 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")) {
                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++;
            }
            if (!redirect->feed_filename[0]) {
                fprintf(stderr, "%s:%d: No URL found for <Redirect>\n",
                        filename, line_num);
                errors++;
            }
            redirect = NULL;
        } else if (!strcasecmp(cmd, "LoadModule")) {
            get_arg(arg, sizeof(arg), &p);
            fprintf(stderr, "%s:%d: Module support not compiled into this version: '%s'\n",
                    filename, line_num, arg);
            errors++;
#endif
Fabrice Bellard's avatar
Fabrice Bellard committed
        } else {
            fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n",
Fabrice Bellard's avatar
Fabrice Bellard committed
                    filename, line_num, cmd);
            errors++;
        }
    }

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

static void show_banner(void)
Fabrice Bellard's avatar
Fabrice Bellard committed
{
    printf("ffserver version " FFMPEG_VERSION ", Copyright (c) 2000-2006 Fabrice Bellard, et al.\n");
}

static void show_help(void)
{
    show_banner();
    printf("usage: ffserver [-L] [-h] [-f configfile]\n"
Fabrice Bellard's avatar
Fabrice Bellard committed
           "Hyper fast multi format Audio/Video streaming server\n"
           "\n"
           "-L            : print the LICENSE\n"
Fabrice Bellard's avatar
Fabrice Bellard committed
           "-h            : this help\n"
           "-f configfile : use configfile instead of /etc/ffserver.conf\n"
           );
}

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

                    /* Turn off any more restarts */
                    feed->child_argv = 0;
            }
        }
    }

    need_to_start_children = 1;
}

Fabrice Bellard's avatar
Fabrice Bellard committed
int main(int argc, char **argv)
{
    const char *config_filename;
    int c;
    struct sigaction sigact;
Fabrice Bellard's avatar
Fabrice Bellard committed

Fabrice Bellard's avatar
Fabrice Bellard committed

    config_filename = "/etc/ffserver.conf";

    my_program_name = argv[0];
    my_program_dir = getcwd(0, 0);
Fabrice Bellard's avatar
Fabrice Bellard committed
    for(;;) {
Fabrice Bellard's avatar
Fabrice Bellard committed
        if (c == -1)
            break;
        switch(c) {
        case 'L':
            show_license();
            exit(0);
Fabrice Bellard's avatar
Fabrice Bellard committed
        case '?':
        case 'h':
            exit(0);
        case 'n':
            no_launch = 1;
            break;
        case 'd':
            ffserver_debug = 1;
Fabrice Bellard's avatar
Fabrice Bellard committed
        case 'f':
            config_filename = optarg;
            break;
        default:
            exit(2);
        }
    }

    putenv("http_proxy");               /* Kill the http_proxy */

    av_init_random(av_gettime() + (getpid() << 16), &random_state);
    /* address on which the server will handle HTTP connections */
    my_http_addr.sin_family = AF_INET;
    my_http_addr.sin_port = htons (8080);
    my_http_addr.sin_addr.s_addr = htonl (INADDR_ANY);

    /* address on which the server will handle RTSP connections */
    my_rtsp_addr.sin_family = AF_INET;
    my_rtsp_addr.sin_port = htons (5454);
    my_rtsp_addr.sin_addr.s_addr = htonl (INADDR_ANY);
Fabrice Bellard's avatar
Fabrice Bellard committed
    nb_max_connections = 5;
Fabrice Bellard's avatar
Fabrice Bellard committed
    first_stream = NULL;
    logfilename[0] = '\0';

    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
    if (parse_ffconfig(config_filename) < 0) {
        fprintf(stderr, "Incorrect config file - exiting.\n");
        exit(1);
    }

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

    /* 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();
            chdir("/");
            close(0);
            open("/dev/null", O_RDWR);
            if (strcmp(logfilename, "-") != 0) {
Fabrice Bellard's avatar
Fabrice Bellard committed
    /* signal init */
    signal(SIGPIPE, SIG_IGN);

    /* open log file if needed */
    if (logfilename[0] != '\0') {
        if (!strcmp(logfilename, "-"))
            logfile = stdout;
        else
            logfile = fopen(logfilename, "w");
    }

    if (http_server() < 0) {
        fprintf(stderr, "Could not start server\n");
Fabrice Bellard's avatar
Fabrice Bellard committed
        exit(1);
    }

    return 0;
}