永发信息网

如何使用nginx做hls直播

答案:2  悬赏:70  手机版
解决时间 2021-11-23 12:09
  • 提问者网友:niaiwoma
  • 2021-11-23 04:37
如何使用nginx做hls直播
最佳答案
  • 五星知识达人网友:西风乍起
  • 2021-11-23 05:00
avconv -i input_file.mp4 -vcodeccopy -acodeccopy -vbsfh264_mp4toannexb
–ss00:00:00 –t00:00:10 output_file.ts

  为例说明ffmpeg如何将命令行参数解析处理。

  int main(int argc,char**argv)

  {

  //初始化参数容器

  OptionsContext o={0};

  //重置参数

  reset_options(&o);

  //解析参数

  parse_options(&o, argc, argv, options,opt_output_file);

  }

  1.重置参数

  staticvoid reset_options(OptionsContext*o)

  依次进行了以下步骤:

  1.1第一步:释放特殊类型

  释放所有的 OPT_SPEC(对应struct SpecifierOpt)和 OPT_STRING (对应 char*)类型的
OptionDef

  代码如下:

  //指向全局变量options

  const OptionDef*po= options;

  //遍历options

  while(po->name){

  //dest指针指向当前option对应的OptionContext中的位置

  void*dst=(uint8_t*)o+ po->u.off;

  //判断是否是SpecifierOpt类型

  if(po->flags& OPT_SPEC){

  //so指向SpecifierOpt*的首地址

  SpecifierOpt **so= dst;

  //获得数组长度

  int i,*count=(int*)(so+1);

  //循环遍历SpecifierOpt*数组

  for(i=0; i<*count; i++){

  //释放SpecifierOpt的specifier(char*类型)

  av_freep(&(*so)[i].specifier);

  //如果OPT类型是字符串,释放SpecifierOpt的u.str(char*类型)

  if(po->flags& OPT_STRING)

  av_freep(&(*so)[i].u.str);

  }

  //释放SpecifierOpt*指针数组

  av_freep(so);

  //重置计数器

  *count=0;

  }

  //判断是否是char*类型

  elseif(po->flags& OPT_OFFSET&& po->flags&
OPT_STRING)

  av_freep(dst);

  po++;

  }

  这里需要对OptionContext的内容做一些说明:

  OptionContext 包含了在视频编转码过程中需要用到的参数,这些参数来自于命令行的输入。

  参数在OptionContext中的存储形式有:

  #defineOPT_INT 0x0080

  #defineOPT_FLOAT 0x0100

  #defineOPT_INT64 0x0400

  #defineOPT_TIME 0x10000

  #defineOPT_DOUBLE 0x20000

  等,详情参见 structOptionDef

  在上述代码中,主要循环释放的是OPT_SPEC(对应struct SpecifierOpt)和 OPT_STRING

  在OptionContext中,OPT_SPEC类型是成对出现的,如下:

  typedefstructOptionsContext{

  int64_t start_time;

  constchar*format;

  SpecifierOpt *codec_names;

  int nb_codec_names;

  SpecifierOpt *audio_channels;

  int nb_audio_channels;

  即:

  SpecifierOpt *xxx_vars;

  int nb_xxx_vars; //nb_读作number_意思是xxx_vars数组的长度

  然后我们来分析对SpecifierOpt*数组的遍历:

  SpecifierOpt **so= dst;

  int i,*count=(int*)(so+1);

  for(i=0; i<*count; i++){

  这里可以这么理解:

  so —指向—> SpecifierOpt *xxx_vars;

  so+1—指向—> int nb_xxx_vars;

  so+1
的含义:so是个SpecifierOpt指针,指针+1则移动了sizeof(SpecifierOpt)的位置,即跳到nb_xxx_vars的位置。

  1.2释放其他类型

  av_freep(&o->stream_maps);

  av_freep(&o->meta_data_maps);

  av_freep(&o->streamid_map);

  这里说一下 av_freep 的用法。

  void av_freep(void*arg)

  {

  void**ptr=(void**)arg;

  av_free(*ptr);

  *ptr=NULL;

  }

  相比传统的free方法,这里主要多做了一步工作:将释放free之后,指针设置为NULL

  同时,要注意到:

  Object *obj;

  free(obj);

  等价用法为:

  av_freep(&obj);

  在ffmpeg中,封装了对应free的方法为:

  void av_free(void*ptr)

  {

  #ifCONFIG_MEMALIGN_HACK

  if(ptr)

  free((char*)ptr-((char*)ptr)[-1]);

  #else

  free(ptr);

  #endif

  }

  这里除了考虑内存对齐之外,跟传统的free方法没有任何变化。

  1.3第三步:设置初始值

  memset(o,0,sizeof(*o));

  o->mux_max_delay =0.7;

  o->recording_time= INT64_MAX;

  o->limit_filesize= UINT64_MAX;

  o->chapters_input_file= INT_MAX;

  不需要过多解释。

  o->mux_max_delay =0.7;

  这一行内容以后在视频切片中会用到。可以调整到更小。

  1.4重新初始化特殊参数

  uninit_opts();

  init_opts();

  这两行代码对应cmdutils.c 文件中的代码段:

  struct SwsContext*sws_opts;

  AVDictionary*format_opts,*codec_opts;

  void init_opts(void)

  {

  #if CONFIG_SWSCALE

  sws_opts= sws_getContext(16,16,0,16,16,0, SWS_BICUBIC,

  NULL,NULL,NULL);

  #endif

  }

  void uninit_opts(void)

  {

  #ifCONFIG_SWSCALE

  sws_freeContext(sws_opts);

  sws_opts=NULL;

  #endif

  av_dict_free(&format_opts);

  av_dict_free(&codec_opts);

  }

  主要进行:
SwsContext*sws_opts,AVDictionary*format_opts,*codec_opts三个全局变量的创建和释放工作。

  2.解析命令行参数

  void parse_options(void*optctx,int argc,char**argv,const OptionDef
*options,void(*parse_arg_function)(void*,constchar*))

  void*optctx,——OptionContext

  int argc,——命令行参数个数

  char**argv,——命令行参数列表

  const OptionDef*options,——选项列表

  void(*parse_arg_function)(void*,constchar*)——自定义的解析方法

  2.1总览

  constchar*opt;

  int optindex, handleoptions=1, ret;

  //处理window的情况

  prepare_app_arguments(&argc,&argv);

  optindex=1;

  //循环处理命令行参数

  while(optindex< argc){

  opt = argv[optindex++];

  //如果传入的参数是“-”打头

  if(handleoptions&& opt[0]=='-'&& opt[1]!='\0'){

  //如果传入的参数是“--”打头

  if(opt[1]=='-'&& opt[2]=='\0'){

  handleoptions =0;

  //略过

  continue;

  }

  //丢弃第一个字符”-”

  opt++;

  //解析命令行参数

  //eg–acodec copy

  //对应的 opt和 argv[optindex]为 “acodec” “copy”

  if((ret= parse_option(optctx, opt, argv[optindex], options))<0)

  exit_program(1);

  optindex += ret;

  }else{

  //此时 opt的值为输出文件名如 test.ts

  if(parse_arg_function)

  //处理输出文件的相关内容,如 struct OutputFile的初始化

  parse_arg_function(optctx, opt);

  }

  }

  在此,ffmpeg 默认的处理输出文件名参数为:

  staticvoid opt_output_file(void*optctx,constchar*filename)

  2.2处理命令行参数

  int parse_option(void*optctx,constchar*opt,constchar*arg, const
OptionDef*options)

  2.2.1查找匹配的Option

  const OptionDef*po;

  int bool_val=1;

  int*dstcount;

  void*dst;

  //从全局变量options数组中查找opt对应的OptionDef

  po = find_option(options, opt);

  //如果未找到且以”no”打头

  //不需要传递参数的选项是bool类型的选项,默认为true

  //如果需要设置为false,则需要加上”no”,以下的if则是处理这种情况

  if(!po->name&& opt[0]=='n'&& opt[1]=='o'){

  //去掉开头的”no”重新查找

  po = find_option(options, opt +2);

  //如果仍未找到或者找到的选项不是bool类型

  if(!(po->name&&(po->flags& OPT_BOOL)))

  //报错

  goto unknown_opt;

  bool_val =0;

  }

  //如果未找到且不是以上的”no”打头情况

  if(!po->name)

  //寻找默认配置进行处理

  po = find_option(options,"default");

  //default配置也未找到,报错

  if(!po->name){

  unknown_opt:

  av_log(NULL, AV_LOG_ERROR,"Unrecognizedoption '%s'\n", opt);

  return AVERROR(EINVAL);

  }

  //如果选项必须有参数但是没有可用的参数,报错

  if(po->flags& HAS_ARG&&!arg){

  av_log(NULL, AV_LOG_ERROR,"Missingargument for option '%s'\n", opt);

  return AVERROR(EINVAL);

  }

  现在来查看一下find_option方法的实现:

  staticconst OptionDef*find_option(const OptionDef*po,constchar*name)

  根据name在全局变量options数组中查找OptionDef

  //这里先处理参数带有冒号的情况。比如 codec:a codec:v等

  constchar*p= strchr(name,':');

  int len= p? p- name: strlen(name);

  //遍历options

  while(po->name!=NULL){

  //比较option的名称与name是否相符。

  //这里 codec 与 codec:a相匹配

  if(!strncmp(name, po->name, len)&& strlen(po->name)==
len)

  break;

  po++;

  }

  return po;

  2.2.2寻找选项地址

  以下的代码用于将 void*dst变量赋值。让dst指向需要赋值的选项地址。

  //如果选项在OptionContext中是以偏移量定位或者是 SpecifierOpt*数组的类型

  dst= po->flags&(OPT_OFFSET| OPT_SPEC)?

  //dst指向从 optctx地址偏移u.off的位置

  (uint8_t*)optctx+ po->u.off:

  //否则直接指向 OptionDef结构中定义的位置

  po->u.dst_ptr;

  //如果选项是SpecifierOpt*数组

  if(po->flags& OPT_SPEC){

  //数组首地址

  SpecifierOpt **so= dst;

  char*p= strchr(opt,':');

  //这里是取得数组的当前长度+1

  //请回顾 1.1中的描述:

  //SpecifierOpt *xxx;

  //int nb_xxx;

  //当so指向xxx时刻,so+1指向nb_xxx

  dstcount =(int*)(so+1);

  //动态增长数组

  *so = grow_array(*so,sizeof(**so), dstcount,*dstcount+1);

  //将创建的SpecifierOpt结构体中的specifier赋值

  //如codec:v 则specifier值为 “v”

  (*so)[*dstcount-1].specifier= av_strdup(p? p+1:"");

  //dst指针指向数组新增的SpecifierOpt中的 u地址

  //此时dstcount的值已经变作新数组的长度,亦即原数组长度+1

  dst =&(*so)[*dstcount-1].u;
全部回答
  • 1楼网友:十年萤火照君眠
  • 2021-11-23 05:27
继续以命令行 avconv -i input_file.mp4 -vcodeccopy -acodeccopy -vbsfh264_mp4toannexb –ss00:00:00 –t00:00:10 output_file.ts 为例说明ffmpeg如何将命令行参数解析处理。 int main(int argc,char**argv) { //初始化参数容器 OptionsContext ...
我要举报
如以上回答内容为低俗、色情、不良、暴力、侵权、涉及违法等信息,可以点下面链接进行举报!
点此我要举报以上问答信息
大家都在看
推荐资讯