ffmpeg+nginx推流

参考博文 https://www.cnblogs.com/wanggang123/p/6056700.html
https://blog.csdn.net/sidumqz/article/details/52830622

在学习hls+nginx的过程中,需要用到ffmeg对源解封装处理,有必要学习ffmpeg.exe是使用

ffmpeg.exe方法

ffmpeg.exe -re -i "E:\avideo.mp4" -vcodec libx264 \
-vprofile baseline -acodec aac -ar 44100 -strict -2 \
-ac 1 -f flv -s 1280x720 -q 10 \
rtmp://%ip%:1935/hls/test2  

ffmpeg.exe是ffmpeg库提供的一个编解码工具,可以对各种音视频进行编解码,并输出不同的封装格式文件。 -re 表示按照时间戳读取视频源文件;输出格式为flv的封装
ffmpeg.c对这些option选项做了处理

int split_commandline(OptionParseContext *octx, int argc, char *argv[],
                      const OptionDef *options,
                      const OptionGroupDef *groups, int nb_groups)
{
    int optindex = 1;
    int dashdash = -2;

    /* perform system-dependent conversions for arguments list */
    prepare_app_arguments(&argc, &argv);

    init_parse_context(octx, groups, nb_groups);
    av_log(NULL, AV_LOG_DEBUG, "Splitting the commandline.\n");

    while (optindex < argc) {
        const char *opt = argv[optindex++], *arg;
        const OptionDef *po;
        int ret;

        av_log(NULL, AV_LOG_DEBUG, "Reading option '%s' ...", opt);

        if (opt[0] == '-' && opt[1] == '-' && !opt[2]) {
            dashdash = optindex;
            continue;
        }
        /* unnamed group separators, e.g. output filename */
        if (opt[0] != '-' || !opt[1] || dashdash+1 == optindex) {
            finish_group(octx, 0, opt);
            av_log(NULL, AV_LOG_DEBUG, " matched as %s.\n", groups[0].name);
            continue;
        }
        opt++;

#define GET_ARG(arg)                                                           \
do {                                                                           \
    arg = argv[optindex++];                                                    \
    if (!arg) {                                                                \
        av_log(NULL, AV_LOG_ERROR, "Missing argument for option '%s'.\n", opt);\
        return AVERROR(EINVAL);                                                \
    }                                                                          \
} while (0)

        /* named group separators, e.g. -i */
        if ((ret = match_group_separator(groups, nb_groups, opt)) >= 0) {
            GET_ARG(arg);
            finish_group(octx, ret, arg);
            av_log(NULL, AV_LOG_DEBUG, " matched as %s with argument '%s'.\n",
                   groups[ret].name, arg);
            continue;
        }

        /* normal options */
        po = find_option(options, opt);
        if (po->name) {
            if (po->flags & OPT_EXIT) {
                /* optional argument, e.g. -h */
                arg = argv[optindex++];
            } else if (po->flags & HAS_ARG) {
                GET_ARG(arg);
            } else {
                arg = "1";
            }

            add_opt(octx, po, opt, arg);
            av_log(NULL, AV_LOG_DEBUG, " matched as option '%s' (%s) with "
                   "argument '%s'.\n", po->name, po->help, arg);
            continue;
        }

        /* AVOptions */
        if (argv[optindex]) {
            ret = opt_default(NULL, opt, argv[optindex]);
            if (ret >= 0) {
                av_log(NULL, AV_LOG_DEBUG, " matched as AVOption '%s' with "
                       "argument '%s'.\n", opt, argv[optindex]);
                optindex++;
                continue;
            } else if (ret != AVERROR_OPTION_NOT_FOUND) {
                av_log(NULL, AV_LOG_ERROR, "Error parsing option '%s' "
                       "with argument '%s'.\n", opt, argv[optindex]);
                return ret;
            }
        }

        /* boolean -nofoo options */
        if (opt[0] == 'n' && opt[1] == 'o' &&
            (po = find_option(options, opt + 2)) &&
            po->name && po->flags & OPT_BOOL) {
            add_opt(octx, po, opt, "0");
            av_log(NULL, AV_LOG_DEBUG, " matched as option '%s' (%s) with "
                   "argument 0.\n", po->name, po->help);
            continue;
        }

        av_log(NULL, AV_LOG_ERROR, "Unrecognized option '%s'.\n", opt);
        return AVERROR_OPTION_NOT_FOUND;
    }

    if (octx->cur_group.nb_opts || codec_opts || format_opts || resample_opts)
        av_log(NULL, AV_LOG_WARNING, "Trailing options were found on the "
               "commandline.\n");

    av_log(NULL, AV_LOG_DEBUG, "Finished splitting the commandline.\n");

    return 0;
}

1.OptionParseContext octx;用来将命令行中的参数通过分析分别存入此结构体的对应变量中。之后argc, argv就没用了。

2.options,定义了所有的参数、参数类型、参数是否有值、是放到全局变量中还是放到OptionsContext结构的某个变量中,或者是调用一个函数来处理参数的值。

按照前篇nginx+ffmpeg的配置好后,可以调试hls以及rtmp的流媒体协议;
如下堆栈对应hls+前篇nginx的讲解,最后通过http新增访问location来设置hls的访问路径http://localhost/hls/test2.m3u 对应路径为http://ip:80/hls/test2.m3u8

#0  hls_probe (p=0x70107f) at libavformat/hls.c:2136
#1  0x00007ffff721db92 in av_probe_input_format3 (pd=0x7fffec33ac20, is_opened=1, score_ret=0x7fffec33aba4)
    at libavformat/format.c:209
#2  0x00007ffff721dd95 in av_probe_input_format2 (pd=0x7fffec33ac20, is_opened=1, score_max=0x7fffec33abf4)
    at libavformat/format.c:252
#3  0x00007ffff721e066 in av_probe_input_buffer2 (pb=0x7fffe400b100, fmt=0x7fffe4000928, 
    filename=0x701060 "http://10.28.9.125:80/hls/test2.m3u8", logctx=0x7fffe4000920, offset=0, 
    max_probe_size=1048576) at libavformat/format.c:332
#4  0x00007ffff7379982 in init_input (s=0x7fffe4000920, filename=0x701060 "http://10.28.9.125:80/hls/test2.m3u8", 
    options=0x7fffec33ace0) at libavformat/utils.c:421
#5  0x00007ffff7379e7a in avformat_open_input (ps=0x7fffec33ad40, 
    filename=0x701060 "http://10.28.9.125:80/hls/test2.m3u8", fmt=0x0, options=0x620c78)
    at libavformat/utils.c:537

分析调用堆栈
ffplay开启读线程read_thread—–> ic = avformat_alloc_context();—->avformat_get_context_defaults—->avformat_open_input()—->init_input—->s->io_open()—->ffio_open_whitelist—->ffurl_alloc—>url_find_protocol—->av_probe_input_buffer2—>av_probe_input_format2 —>av_probe_input_format3 —>read_probe—>最终找到hls->probe

发布了38 篇原创文章 · 获赞 5 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/zhiyanzhai563/article/details/79895820