Skip to content
Snippets Groups Projects
ffserver.c 149 KiB
Newer Older
  • Learn to ignore specific revisions
  •                 fprintf(stderr, "%s:%d: Unknown AudioCodec: %s\n",
    
                            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) {
    
                    fprintf(stderr, "%s:%d: Unknown VideoCodec: %s\n",
    
            } else if (!strcasecmp(cmd, "MaxTime")) {
                get_arg(arg, sizeof(arg), &p);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            } else if (!strcasecmp(cmd, "AudioBitRate")) {
                get_arg(arg, sizeof(arg), &p);
    
    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) {
    
                    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
                }
            } 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, "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;
                }
                if (opt_default(arg, arg2, avctx, type|AV_OPT_FLAG_ENCODING_PARAM)) {
                    fprintf(stderr, "AVOption error: %s %s\n", arg, arg2);
                    errors++;
                }
    
            } 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 = av_mallocz(sizeof(*nacl));
    
    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++;
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
                    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
                    }
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
                    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++;
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
                    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 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;
    }
    
    
    static void opt_debug()
    {
        ffserver_debug = 1;
        ffserver_daemon = 0;
    }
    
    static void opt_show_help(void)
    {
        printf("usage: ffserver [options]\n"
               "Hyper fast multi format Audio/Video streaming server\n");
        printf("\n");
        show_help_options(options, "Main options:\n", 0, 0);
    }
    
    static const OptionDef options[] = {
    
        { "h", OPT_EXIT, {(void*)opt_show_help}, "show help" },
    
        { "version", OPT_EXIT, {(void*)show_version}, "show version" },
    
        { "L", OPT_EXIT, {(void*)show_license}, "show license" },
    
        { "formats", OPT_EXIT, {(void*)show_formats}, "show available formats, codecs, protocols, ..." },
    
        { "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
    int main(int argc, char **argv)
    {
    
        struct sigaction sigact;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    
    
    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);
    
        parse_options(argc, argv, options, NULL);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    
    
        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, "a");
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        }
    
    
        if (http_server() < 0) {
            fprintf(stderr, "Could not start server\n");
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            exit(1);
        }
    
        return 0;
    }