如何用nginx+nginx ffmpeg 不工作实现苹果HLS协议

如何用nginx+ffmpeg实现苹果HLS协议
我的图书馆
如何用nginx+ffmpeg实现苹果HLS协议
请选中您要保存的内容,粘贴到此文本框
今年用三个月时间做了一个支持HLS的视频服务,用了三个月时间,对于一个视频处理的门外汉来说,是一个相当痛苦和漫长的过程,因此想抽时间将开发过程重新梳理一边,顺边形成一个不多篇幅但是足够细致的回顾和说明文档。
当前只是一个草稿,不做任何整理,所以不会强调文章的连贯和呼应关系。
大致叙述的内容包括:
HLS协议的理解
nginx ffmpeg的编译 安装 调试,以及工具的选择 使用 gdb等
nginx模块开发
ffmpeg的开发
重点将集中在 ffmpeg 的开发上。
HLS协议的实现有很多的细节,比如我在实际的开发过程中,就面临将多种不同格式的视频源文件(来源于不同的编码器以及有不同的profile)动态切片输出。而现有能在网上找到的方式基本都是对视频文件做了预先处理,比如用ffmpeg将视频文件先转换成物理存储的mpeg2ts文件,然后用nginx进行动态切片输出。这对开发带来了很大的困难。
如果我们将问题简化的话,即 输入文件为 mp4 (isom512 , 2 channels stereo),那么最简单的实现方式是如下命令行:
input_file.mp4 -copy -copy -h264_mp4toannexb –00:00:00 –00:00:10 output_file.ts
然后通过 对 参数 –00:00:00 –00:00:10 的调整,获得多个物理切片,提供给nginx输出。
这里需要提供一个细节,即 处理的性能。 所以在上述的命令行中,仅仅进行了 remux 而没有任何 ecode 和 decode 的操作。
我们要做的,就是将这行命令变成 可供 nginx 调用的 api。
当然,任然可以选择最简单的作法,nginx模块里面调用系统命令。不过这样子,貌似有点儿寒碜吧。呵呵。
所以,我们需要的是这样一个接口:
int segment(byte** output, int *output_len, int start, int end, const char * inputfile)
从命令行到接口方法,第一步就是弄懂ffmpeg如何解析命令行参数并赋值
ffmpeg参数解析
——此文档为《如何用nginx+ffmpeg实现苹果HLS协议》的一个章节。
谢绝对非技术问题的修改,转载请注明来源
继续以命令行
input_file.mp4 -copy -copy -h264_mp4toannexb –00:00:00 –00:00:10 output_file.ts
为例说明ffmpeg如何将命令行参数解析处理。
int mainint argccharargv
&&& OptionsContext o0
&&& reset_optionso
&&& parse_optionso argc argv optionsopt_output_file
1.重置参数
staticvoid reset_optionsOptionsContexto
依次进行了以下步骤:
1.1第一步:释放特殊类型
释放所有的 OPT_SPEC(对应 SpecifierOpt)和 OPT_STRING (对应 char*)类型的 OptionDef
代码如下:
&&& const OptionDefpo options
&&& whileponame
//destOptionContext
&&&&&&& voiddstuint8_to pouoff
&&&&&& SpecifierOpt
&&&&&&& ifpoflags OPT_SPEC
//soSpecifierOpt*
&&&&&&&&&&& SpecifierOpt so dst
&&&&&&&&&&& int icountintso1
//SpecifierOpt*
&&&&&&&&&&& fori0 icount i
//SpecifierOptspecifierchar*类型
&&&&&&&&&&&&&&&av_freepsoispecifier
//SpecifierOptu.strchar*类型
&&&&&&&&&&&&&&&ifpoflags OPT_STRING
&&&&&&&&&&&&&&&&&&&av_freepsoiustr
&&&&&&&&&&&
//SpecifierOpt*
&&&&&&&&&&& av_freepso
&&&&&&&&&&& count0
&&&&&& char*
elseifpoflags OPT_OFFSET poflags OPT_STRING
&&&&&&&&&&& av_freepdst
&&&&&&& po
这里需要对OptionContext的内容做一些说明:
OptionContext 包含了在视频编转码过程中需要用到的参数,这些参数来自于命令行的输入。
参数在OptionContext中的存储形式有:
#defineOPT_INT&&&0x0080
#defineOPT_FLOAT&0x0100
#defineOPT_INT64&0x0400
#defineOPT_TIME&0x10000
#defineOPT_DOUBLE 0x20000
等,详情参见 structOptionDef
在上述代码中,主要循环释放的是OPT_SPEC(对应 SpecifierOpt)和 OPT_STRING
在OptionContext中,OPT_SPEC类型是成对出现的,如下:
typedefstructOptionsContext
&&& int64_t start_time
&&& constcharformat
&&& SpecifierOpt *codec_names
&&& int&&&&&&&nb_codec_names
&&& SpecifierOpt *audio_channels
&&& int&&&&&&&nb_audio_channels
&&& SpecifierOpt *xxx_vars
&&& int&&&&&&&nb_xxx_vars//nb_
然后我们来分析对SpecifierOpt*数组的遍历:
&&&&&&&&&&& SpecifierOpt so dst
&&&&&&&&&&& int icountintso1
&&&&&&&&&&& fori0 icount i
这里可以这么理解:
&&&SpecifierOpt *xxx_vars
&&&int&&&&&&&nb_xxx_vars
so是个SpecifierOpt指针,指针+1则移动了sizeof(SpecifierOpt)的位置,即跳到nb_xxx_vars的位置。
1.2释放其他类型
&&& av_freepostream_maps
&&& av_freepometa_data_maps
&&& av_freepostreamid_map
这里说一下 av_freep 的用法。
void av_freepvoidarg
&&& voidptrvoidarg
&&& av_freeptr
&&& ptrNULL
相比传统的free方法,这里主要多做了一步工作:将释放free之后,指针设置为NULL
同时,要注意到:
Object obj
等价用法为:
av_freepobj
在ffmpeg中,封装了对应free的方法为:
void av_freevoidptr
#ifCONFIG_MEMALIGN_HACK
&&&&&&& freecharptrcharptr1
&&& freeptr
这里除了考虑内存对齐之外,跟传统的free方法没有任何变化。
1.3第三步:设置初始值
&&& memseto0sizeofo
&&& omux_max_delay&<SPAN style="COLOR: #ff
&&& orecording_time INT64_MAX
&&& olimit_filesize UINT64_MAX
&&& ochapters_input_file INT_MAX
不需要过多解释。
omux_max_delay&<SPAN style="COLOR: #ff
这一行内容以后在视频切片中会用到。可以调整到更小。
1.4重新初始化特殊参数
&&& uninit_opts
&&& init_opts
这两行代码对应cmdutils.c 文件中的代码段:
struct SwsContextsws_opts
AVDictionaryformat_optscodec_opts
void init_optsvoid
#if CONFIG_SWSCALE
&&& sws_opts sws_getContext1616016160 SWS_BICUBIC
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&NULLNULLNULL
void uninit_optsvoid
#ifCONFIG_SWSCALE
&&& sws_freeContextsws_opts
&&& sws_optsNULL
&&& av_dict_freeformat_opts
&&& av_dict_freecodec_opts
主要进行: SwsContextsws_optsAVDictionaryformat_optscodec_opts三个全局变量的创建和释放工作。
2.解析命令行参数
void parse_optionsvoidoptctxint argccharargvconst OptionDef optionsvoidparse_arg_functionvoidconstchar
voidoptctx——OptionContext
int argc——命令行参数个数
charargv——命令行参数列表
const OptionDefoptions——选项列表
voidparse_arg_functionvoidconstchar——自定义的解析方法
&&& constcharopt
&&& int optindex handleoptions1 ret
&&& prepare_app_argumentsargcargv
&&& optindex1
&&& whileoptindex argc
&&&&&&& opt
argvoptindex
&&&&&&& ifhandleoptions opt0'-' opt1'\0'
&&&&&&&&&&& ifopt1'-' opt2'\0'
&&&&&& &&&&&&&&&handleoptions 0
&&&&&&&&&&&&&&&continue
&&&&&&&&&&&
&&&&&&&&&&& opt
&&&&&&&&&&
//eg–acodec copy
&&&&&&&&&&& ifret parse_optionoptctx opt argvoptindex options0
&&&&&&&&&&&&&&&exit_program1
&&&&&&&&&&& optindex
&&&&&&& else
&&&&&&&&&&& ifparse_arg_function
&&&&&&&&&&&&&&&parse_arg_functionoptctx opt
在此,ffmpeg 默认的处理输出文件名参数为:
staticvoid opt_output_filevoidoptctxconstcharfilename
2.2处理命令行参数
int parse_optionvoidoptctxconstcharoptconstchararg&&&&&&&&&&&&&&&&&const OptionDefoptions
2.2.1查找匹配的Option
&&& const OptionDefpo
&&& int bool_val1
&&& intdstcount
&&& voiddst
find_optionoptions opt
&&& ifponame opt0'n' opt1'o'
&&&&&&& po
find_optionoptions opt 2
&&&&&&& ifponamepoflags OPT_BOOL
&&&&&&&&&&& goto unknown_opt
&&&&&&& bool_val 0
&&& ifponame
&&&&&&& po
find_optionoptions"default"
&&& ifponame
unknown_opt
&&&&&&& av_logNULL AV_LOG_ERROR"Unrecognizedoption '%s'\n" opt
&&&&&&& return AVERROREINVAL
&&& ifpoflags HAS_ARGarg
&&&&&&& av_logNULL AV_LOG_ERROR"Missingargument for option '%s'\n" opt
&&&&&&& return AVERROREINVAL
现在来查看一下find_option方法的实现:
staticconst OptionDeffind_optionconst OptionDefpoconstcharname
根据name在全局变量options数组中查找OptionDef
&&& constcharp strchrname':'&
&&& int len p p name strlenname
&&& whileponameNULL
&&&&&&& ifstrncmpname poname len strlenponame len
&&&&&&&&&&& break
&&&&&&& po
&&& return po
2.2.2寻找选项地址
以下的代码用于将 void*dst变量赋值。让dst指向需要赋值的选项地址。
//OptionContext SpecifierOpt*
&&& dst poflagsOPT_OFFSET OPT_SPEC
uint8_toptctx pouoff
poudst_ptr
&&& SpecifierOpt*
&&& ifpoflags OPT_SPEC
&&&&&&& SpecifierOpt so dst
&&&&&&& charp strchropt':'
&&& SpecifierOpt *xxx
int nb_xxx
&&&&&&& dstcount intso1
&&&&&&& so
grow_arraysosizeofso dstcountdstcount1
//SpecifierOptspecifier
//specifier
&&&&&&& sodstcount1specifier av_strdupp p1""
//dstSpecifierOpt
//dstcount
&&&&&&& dst sodstcount1u
&&& av_logNULL AV_LOG_ERROR"parse_option-&'%s' '%s' %d %d %d\n" opt arg
&&&&&&& poflags OPT_SPEC
&&&&&&& poflags OPT_STRING
&&&&&&& poufunc_arg10
在此做出一些说明:
&&& dst poflagsOPT_OFFSET OPT_SPEC
uint8_toptctx pouoff
poudst_ptr
关于poudst_ptr的指向,在ffmpeg中都是用来设置全局变量使用。如以下代码:
staticint exit_on_error0
staticconst OptionDef options
&&& "xerror" OPT_BOOLvoidexit_on_error"exit on error""error"
poudst_ptr (voidexit_on_error)
所以之后的对poudst_ptr赋值也就是对avconv.c中定义的全局变量赋值。
关于so grow_arraysosizeofso dstcountdstcount1
用户数组动态增长方法签名如下:
voidgrow_arrayvoidarrayint elem_sizeintsizeint new_size
其内在处理逻辑如下:
&&&&&&& uint8_t tmp av_reallocarray new_sizeelem_size
&&&&&&& memsettmpsizeelem_size0new_sizesize elem_size
&&&&&&& size
&&&&&&& return tmp
需要注意到的是int elem_size在当前的上下文中指的是sizeofso==sizeofSpecifierOpt
2.2.3选项赋值
&&&&&& 在获得需要赋值的变量地址之后,接下来的代码流程用于赋值处理,主要是根据变量类型进行赋值:
&&& ifpoflags OPT_STRING
&&&&&&& charstr
&&&&&&& str
av_strduparg
&&&&&&& chardst str
&&& elseifpoflags OPT_BOOL
&&&&&&& intdst bool_val
&&& elseifpoflags OPT_INT
&&&&&&& intdst parse_number_or_dieopt arg OPT_INT64 INT_MIN INT_MAX
&&& elseifpoflags OPT_INT64
&&&&&&& int64_tdst parse_number_or_dieopt arg OPT_INT64 INT64_MIN INT64_MAX
&&& elseifpoflags OPT_TIME
&&&&&&& int64_tdst parse_time_or_dieopt arg1
&&& elseifpoflags OPT_FLOAT
&&&&&&& floatdst parse_number_or_dieopt arg OPT_FLOATINFINITY INFINITY
&&& elseifpoflags OPT_DOUBLE
&&&&&&& doubledst parse_number_or_dieopt arg OPT_DOUBLEINFINITY INFINITY
&&& elseifpoufunc_arg
&&&&&&& int ret
poflags OPT_FUNC2 poufunc2_argoptctx opt arg
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& poufunc_argopt arg
&&&&&&& ifret0
&&&&&&&&&&& av_logNULL AV_LOG_ERROR"Failed to set value '%s' for option '%s'\n" arg opt
&&&&&&&&&&& return ret
&&& ifpoflags OPT_EXIT
&&&&&&& exit_program0
&&& returnpoflags HAS_ARG
最后对ifpoufunc_arg的方法调用再次说明:
如acodec选项定义:
"acodec" HAS_ARG OPT_AUDIO OPT_FUNC2voidopt_audio_codec"force audio codec ('copy' to copy stream)""codec"
我们可以看到,在全局变量options中注册的解析方法为:opt_audio_codec。
现在回到文档开头提到的
input_file.mp4 -copy -copy -h264_mp4toannexb –00:00:00 –00:00:10 output_file.ts
的解析上来。下面,将以比较特殊的 - copy 说明。
首先,在全局变量options中定义了acodec选项的相关信息:
"acodec" HAS_ARG OPT_AUDIO OPT_FUNC2voidopt_audio_codec"force audio codec ('copy' to copy stream)""codec"
可以看到:此选项:
1.&&&&&&有参数需要传入
2.&&&&&&处理的是音频数据
3.&&&&&&解析方式是自定义方法
4.&&&&&&解析方法为: opt_audio_codec
5.&&&&&&其功能是:"forceaudio codec ('copy' to copy stream)"
6.&&&&&&其对应的命令行名称为codec
因此,在parse_option的调用中,对于acodec选项,将用opt_audio_codec解析方法进行处理。
opt_audio_codecoptctx“acodec”“copy”
方法代码如下:
staticint opt_audio_codecOptionsContextoconstcharoptconstchararg
&&& return parse_optiono"codec:a" arg options
可以看到,在这里,没有做更多的工作,只是对命令行选项acodec进行了一个转换,使用的解析器进行重新解析:
opt_audio_codecoptctx“codec:a”“copy”
这里需要回顾一下方法
staticconst OptionDeffind_optionconst OptionDefpoconstcharname
此方法是在查找name为“codec:a” 的option 时,实际是寻找的 “codec”
"codec" HAS_ARG OPT_STRING OPT_SPECoff OFFSETcodec_names"codec name""codec"
可以看到:此选项:
1.&&&&&&有参数需要传入
2.&&&&&&处理的是类型的数组(SpecifierOpt*)
3.&&&&&&SpecifierOpt 结构体存储的是(char *)
4.&&&&&&赋值方式是直接赋值,偏移位是:off OFFSETcodec_names,亦即:
typedefstruct OptionsContext
&&& /* input/output options */
&&&int64_t start_time
&&& constcharformat
&&& SpecifierOptcodec_names
&&& int&&&&&&&nb_codec_names
5.&&&&&& 其功能是:"codec name"
6.&&&&&&其对应的命令行名称为codec
因此,在调用
parse_optiono"codec:a""copy",options
之后,获得的结果是:
typedefstruct SpecifierOpt
&&& charspecifier
&&&&&&& uint8_t str
&&&&&&& int&&&&&&&i
&&&&&&& int64_t&i64
&&&&&&& float&&&&& f
&&&&&&& double&& dbl
SpecifierOpt
而在OptionsContext中,
typedefstruct OptionsContext
&&& /* input/output options */
&&& int64_t start_time
&&& constcharformat
&&& SpecifierOptcodec_names
&&& int&&&&&&&nb_codec_names
通过本篇的分析,基本可以明了ffmpeg在输入参数的解析流程。这对我们之后想要把命令行
input_file.mp4 -copy -copy -h264_mp4toannexb –00:00:00 –00:00:10 output_file.ts
转换为可用的内嵌代码提供了一个很好的入口和分析点。
在之后的章节中,我们会从此全面进入avconv.c 的世界。
同时,需要指出的是,本章节没有描述两个重要的解析部分:
staticconst OptionDef options
&&& "i" HAS_ARG OPT_FUNC2voidopt_input_file"input file name""filename"
parse_optionso argc argv optionsopt_output_file
这里涉及到的两个解析方法为:
staticint opt_input_fileOptionsContextoconstcharoptconstcharfilename
staticvoid opt_output_filevoidoptctxconstcharfilename
这两个方法除了进行Option设置之外,还对输入输出的对应结构和变量进行了初始化,其功能和重要性已经超出了简单的命令行解析的范围,因此,将在后继章节中分析。转自:http://blog.csdn.net/deltatang/article/details/7931827
TA的最新馆藏[转]&[转]&[转]&
喜欢该文的人也喜欢简介:HTTP Live Streaming(缩写是 HLS)是一个由苹果公司提出的基于HTTP的流媒体 网络传输协议。
是苹果公司QuickTime X和iPhone软件系统的一部分。它的工作原理是把整个流分成一个个小的基于HTTP
的文件来下载,每次只下载一些。当媒体流正在播放时,客户端可以选择从许多不同的备用源中以不同的速
率下载同样的资源,允许流媒体会话适应不同的数据速率。在开始一个流媒体会话时,客户端会下载一个包
含元数据的extended M3U (m3u8) playlist文件,用于寻找可用的媒体流。
HLS只请求基本的HTTP报文,与实时传输协议(RTP)不同,HLS可以穿过任何允许HTTP数据通过的防
火墙或者代理服务器。它也很容易使用内容分发网络来传输媒体流。
此协议详细内容请参考apple官方网站:
有两种方式搭建HLSserver,
一种是利用apple SDK,
一种是利用adobe 的fms,4.5版本支持hls,参考,
adobe的fms现在很强大,但是商用需要licence。有兴趣的可以研究下。
一种是利用opensouce.我比较喜欢这一种。
opensource的方法主要是使用m3u8-segmenter&#43;ffmpeg对ts文件进行分片。
因此思路就是:
1,用编译好的ffmpeg制作所需要的ts文件,
2,安装libavformat-dev版本,
3,编译m3u8-segmenter,
4,部署到nginx
5,高级功能,流切换
1,本来想下载ffmpeg源码编译,但是因为要涉及到faac,x264,lame库。有时候ffmpeg版本对这些库的版本又有最低版本要求,在编译
faac时候遇到以下问题
安装支持库
apt-get install automake autoconf m4 libtool
-bash: ./bootstrap: /bin/sh^M: bad interpreter: No such file or directory
转换字符:
dos2unix bootstrap
错误:mpeg4ip.h:126:58: error: new declaration ‘char* strcasestr(const char*, const char*)’
解决方法:Remove line 126 containing strcasecmp from mpeg4ip.h as a temporary workaround
make install时遇到
usr/local/share/man/man1文件夹无法创建问题。
最后一个问题无法解决,好像是linux(ubuntu)下同一个目录下,如果已经有一个文件了,则不能创建同名文件夹,遂放弃编译,
直接从ffmpeg网站:http://ffmpeg.org/download.html ,的linux下载页面下载编译好ffmpeg静态文件。这个静态文件的主要目的是
为了把各种文件转换成apple所规定的文件。所以需要AAC,mp3,x264库支持。
或者干脆按照2的方法。apt-get install ffmpeg.这样会得到ffmpeg可行性文件。
2,安装ffmpeg支持库,主要用于编译m3u8-segmenter,这里的ffmpeg支持库,其目的是给segmenter提供libavformat支持。不涉及编解码。
apt-get install libavformat-dev.
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following extra packages will be installed:
libavcodec-dev libavcodec53 libavformat53 libavutil-dev libavutil51 libgsm1 libogg0 liborc-0.4-0
libschroedinger-1.0-0 libspeex1 libtheora0 libva1 libvorbis0a libvorbisenc2 libvpx1
Suggested packages:
libfaad-dev libgsm1-dev libogg-dev libschroedinger-dev libspeex-dev libtheora-dev libvorbis-dev libx11-dev
libxext-dev libraw1394-dev libdc1394-22-dev speex
The following NEW packages will be installed:
libavcodec-dev libavcodec53 libavformat-dev libavformat53 libavutil-dev libavutil51 libgsm1 libogg0 liborc-0.4-0
libschroedinger-1.0-0 libspeex1 libtheora0 libva1 libvorbis0a libvorbisenc2 libvpx1
这样会自动安装ffmepg几个相关库。
3,从&下载m3u8-segmenter
下载后不要用它的反复编译,直接取m3u8-segmenter.c文件,
gcc -Wall -g segmenter.c -o segmenter -lavformat
从源码来看,因为只用到了avformat库,所以只链接这一个即可。生成segmenter文件,用help命令,可以看到已经成功。
HTTP Live Streaming - Segments TS file and creates M3U8 index.
-i, --input FILE
TS file to segment (Use - for stdin)
-d, --duration SECONDS
Duration of each segment (default: 10 seconds)
-p, --output-prefix PREFIX
Prefix for the TS segments, will be appended
with -1.ts, -2.ts etc
-m, --m3u8-file FILE
M3U8 output filename
-u, --url-prefix PREFIX
Prefix for web address of segments, e.g. http://example.org/video/
-n, --num-segment NUMBER
Number of segments to keep on disk
-h, --help
从上图来看,语法很简单,这里贴一个我用的。
./segmenter -i test.ts -n 30 -p sample_test -m stream-test.m3u8 -u http://192.168.1.10:8080/hls/
i表示输入文件,n表示切割30个,p表示切割文件的前缀。m表示生成的m3u8文件名,u表示这些切割后的文件处于web server的哪个目录下,这个一定要和web目录匹配
4,部署到nginx。
nginx的相关部署我在前两个博客中已经详细说明,这里在jwplayer博客的基础上部署hls。
1)目录问题:
在html/jwplayer目录下,建立hls文件夹,将m3u8文件和切割后的全部ts文件拷贝到此目录下,
在VLC PLAYER或者ipad safie浏览器或者在ffplayer(我用的是0.11版本的windows编译版本)
上的访问路径应该是http://192.168.1.10:8080/hls/stream-test.m3u8
2)文件类型问题:编辑&/usr/local/nginx/conf/mime.types 文件,添加如下类型
application/x-mpegURL
video/MP2T
3)重启nginx&
输入上述路径,你应该就看到视频了。
5,高级功能,流切换
上述m3u8文件,只有一个流,不具备流切换功能。在优酷上,如果是ipad客户端,可以看到有标清,高清,超清的按钮,其实那个是对应着不同标准
的(单个)m3u8文件,来实现流切换的,不知道apple是不是这样做的,apple好像是要求“智能”流切换。即不要求用户去选择,而是根据网络状况自适应的。
apple给的sample的流切换是把各个流的m3u8写在一个m3u8文件里实现的。
类&#20284;于这样,其实原理是一样的。
#EXT-X-STREAM-INF:PROGRAM-ID=1, BANDWIDTH=100000
video1/index1.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1, BANDWIDTH=200000
video2/index2.m3u8
如果再继续搭建一个页面,把上述地址嵌在页面里面,这样配合CSS就比较美观了。页面可以在这个基础上,用webpy去做。
页面参考:
苹果开发网:
博客:/linux/459.html
ffmpeg开发网:http://ffmpeg.org/download.html
segmenter源码:
adboe fms介绍:
2014.02备注:
文中所描述的切片软件可能有问题,可选择这个
本文已收录于以下专栏:
相关文章推荐
之前写了一篇基于nginx的hls点播系统,本质上是把一个媒体文件做成m3u8索引,对应的文件都是提前做好放在服务器上的。
nginx充当的是个Http 服务器的角色,之所以说是基于nginx的,是...
Ubuntu 14.04 server
nginx-1.8.1
nginx-rtmp-module
nginx的服务器的搭建安装nginx的依赖库sudo apt-get update
2.nginx配置
Nginx可以支持多虚机配置,如果是一个ip或域名多虚机的情况,就是要不同的虚机对应不同的端口服务,而如果是多ip或域名一个虚机的情况,则又不一样。这里的实际情况就是,80和80...
经过实践,通过h5 video 直接m3u8直播, ios 是都没有问题的。android 4.2以上才基本上没问题。4.2以下的各品牌有些问题需要做不同的兼容。
HTTP Live Strea...
共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式。两个不同进程A、B共享内存的意思是,同一块物理内存被映射到进程A、B各自的进程地址空间。进程A可以即时看到进程B对共享内存中数据的更新,反...
方案一:ffmpeg+nginx新的ffmpeg已经支持HLS。点播生成hls分片:
-c:v libx264 -c:a -f hls /usr/local/nginx/h...
4.客户端接收文件
4.1服务端的实现
对于服务端,我们只需要实现上一章遗留的sendFile()方法就可以了,它起初在handleProtocol中是注释掉的。另外,由于创建连接、获取流...
一、首先保证nginx能正常运行:
&#160; &#160; &#160; &#160;&#160;
&#160; &#160; &#160; &#160; &#160;这个就是因为前面我们把nginx的目录加到了Path中,然而nginx启动时各种路径都是以当前工作目录为起始点的,这就导致了...
至目前位置,HLS 是移动平台上非常重要并十分流行的流媒体传输协议。做移动平台的流媒体开发,不知道它不掌握它 ,真是一大遗憾。而HLS的平台搭建有一定的难度,本文针对对该方向有一定了解的朋友,将方案实...
分类:&#160;视频开发 HLS协议
12:16&#160;2063人阅读&#160;评论(3)&#160;收藏&#160;举报
目录(?)[+]
HLS (HTTP Live S...
他的最新文章
讲师:姜飞俊
讲师:汪木铃
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)今年用三个月时间做了一个支持HLS的视频服务,用了三个月时间,对于一个视频处理的门外汉来说,是一个相当痛苦和漫长的过程,因此想抽时间将开发过程重新梳理一边,顺边形成一个不多篇幅但是足够细致的回顾和说明文档。
当前只是一个草稿,不做任何整理,所以不会强调文章的连贯和呼应关系。
大致叙述的内容包括:
HLS协议的理解
nginx ffmpeg的编译 安装 调试,以及工具的选择 使用 gdb等
nginx模块开发
ffmpeg的开发
重点将集中在 ffmpeg 的开发上。
HLS协议的实现有很多的细节,比如我在实际的开发过程中,就面临将多种不同&#26684;式的视频源文件(来源于不同的编码器以及有不同的profile)动态切片输出。而现有能在网上找到的方式基本都是对视频文件做了预先处理,比如用ffmpeg将视频文件先转换成物理存储的mpeg2ts文件,然后用nginx进行动态切片输出。这对开发带来了很大的困难。
如果我们将问题简化的话,即 输入文件为 mp4 (isom512 , 2 channels stereo),那么最简单的实现方式是如下命令行:
avconv&&input_file.mp4&-copy&-copy&-h264_mp4toannexb&–00:00:00&–00:00:10&output_file.ts
然后通过 对 参数&–00:00:00&–00:00:10&的调整,获得多个物理切片,提供给nginx输出。
这里需要提供一个细节,即 处理的性能。 所以在上述的命令行中,仅仅进行了 remux 而没有任何 ecode 和 decode 的操作。
我们要做的,就是将这行命令变成 可供 nginx 调用的 api。
当然,任然可以选择最简单的作法,nginx模块里面调用系统命令。不过这样子,貌&#20284;有点儿寒碜吧。呵呵。
所以,我们需要的是这样一个接口:
int segment(byte** output, int *output_len, int start, int end, const char * inputfile)
从命令行到接口方法,第一步就是弄懂ffmpeg如何解析命令行参数并赋&#20540;
ffmpeg参数解析
——此文档为《如何用nginx&#43;ffmpeg实现苹果HLS协议》的一个章节。
谢绝对非技术问题的修改,转载请注明来源
继续以命令行
avconv&&input_file.mp4&-copy&-copy&-h264_mp4toannexb&–00:00:00&–00:00:10&output_file.ts
为例说明ffmpeg如何将命令行参数解析处理。
int&mainint&argccharargv
&&&&OptionsContext o0
&&&&reset_optionso
&&&&parse_optionso&argc&argv&optionsopt_output_file
1.重置参数
staticvoid&reset_optionsOptionsContexto
依次进行了以下步骤:
1.1第一步:释放特殊类型
释放所有的&OPT_SPEC(对应&SpecifierOpt)和&OPT_STRING&(对应&char*)类型的&OptionDef
代码如下:
&&&&const&OptionDefpo&options
&&&&whileponame
//destOptionContext
&&&&&&&&voiddstuint8_to&pouoff
&&&&&&&SpecifierOpt
&&&&&&&&ifpoflags&OPT_SPEC
//soSpecifierOpt*
&&&&&&&&&&&&SpecifierOpt&so&dst
&&&&&&&&&&&&int&icountintso1
//SpecifierOpt*
&&&&&&&&&&&&fori0&icount&i
//SpecifierOptspecifierchar*类型
&&&&&&&&&&&&&&&av_freepsoispecifier
//SpecifierOptu.strchar*类型
&&&&&&&&&&&&&&&ifpoflags&OPT_STRING
&&&&&&&&&&&&&&&&&&&av_freepsoiustr
&&&&&&&&&&&&
//SpecifierOpt*
&&&&&&&&&&&&av_freepso
&&&&&&&&&&&&count0
&&&&&&&char*
elseifpoflags&OPT_OFFSET&poflags&OPT_STRING
&&&&&&&&&&&&av_freepdst
&&&&&&&&po
这里需要对OptionContext的内容做一些说明:
OptionContext&包含了在视频编转码过程中需要用到的参数,这些参数来自于命令行的输入。
参数在OptionContext中的存储形式有:
#defineOPT_INT&&&0x0080
#defineOPT_FLOAT&0x0100
#defineOPT_INT64&0x0400
#defineOPT_TIME&0x10000
#defineOPT_DOUBLE 0x20000
等,详情参见&structOptionDef
在上述代码中,主要循环释放的是OPT_SPEC(对应&SpecifierOpt)和&OPT_STRING
在OptionContext中,OPT_SPEC类型是成对出现的,如下:
typedefstructOptionsContext
&&&&int64_t start_time
&&&&constcharformat
&&&&SpecifierOpt *codec_names
&&&&int&&&&&&&nb_codec_names
&&&&SpecifierOpt *audio_channels
&&&&int&&&&&&&nb_audio_channels
&&&&SpecifierOpt *xxx_vars
&&&&int&&&&&&&nb_xxx_vars//nb_
然后我们来分析对SpecifierOpt*数组的遍历:
&&&&&&&&&&&&SpecifierOpt&so&dst
&&&&&&&&&&&&int&icountintso1
&&&&&&&&&&&&fori0&icount&i
这里可以这么理解:
&&&SpecifierOpt
&&&int&&&&&&&nb_xxx_vars
so是个SpecifierOpt指针,指针+1则移动了sizeof(SpecifierOpt)的位置,即跳到nb_xxx_vars的位置。
1.2释放其他类型
&&&&av_freepostream_maps
&&&&av_freepometa_data_maps
&&&&av_freepostreamid_map
这里说一下&av_freep&的用法。
void&av_freepvoidarg
&&&&voidptrvoidarg
&&&&av_freeptr
&&&&ptrNULL
相比传统的free方法,这里主要多做了一步工作:将释放free之后,指针设置为NULL
同时,要注意到:
Object&obj
等价用法为:
av_freepobj
在ffmpeg中,封装了对应free的方法为:
void&av_freevoidptr
#ifCONFIG_MEMALIGN_HACK
&&&&&&&&freecharptrcharptr1
&&&&freeptr
这里除了考虑内存对齐之外,跟传统的free方法没有任何变化。
1.3第三步:设置初始&#20540;
&&&&memseto0sizeofo
&&&&omux_max_delay&0.7
&&&&orecording_time&INT64_MAX
&&&&olimit_filesize&UINT64_MAX
&&&&ochapters_input_file&INT_MAX
不需要过多解释。
omux_max_delay&0.7
这一行内容以后在视频切片中会用到。可以调整到更小。
1.4重新初始化特殊参数
&&&&uninit_opts
&&&&init_opts
这两行代码对应cmdutils.c 文件中的代码段:
struct&SwsContextsws_opts
AVDictionaryformat_optscodec_opts
void&init_optsvoid
#if CONFIG_SWSCALE
&&&&sws_opts&sws_getContext1616016160&SWS_BICUBIC
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&NULLNULLNULL
void&uninit_optsvoid
#ifCONFIG_SWSCALE
&&&&sws_freeContextsws_opts
&&&&sws_optsNULL
&&&&av_dict_freeformat_opts
&&&&av_dict_freecodec_opts
主要进行:&SwsContextsws_optsAVDictionaryformat_optscodec_opts三个全局变量的创建和释放工作。
2.解析命令行参数
void&parse_optionsvoidoptctxint&argccharargvconst&OptionDef&optionsvoidparse_arg_functionvoidconstchar
voidoptctx——OptionContext
int&argc——命令行参数个数
charargv——命令行参数列表
const&OptionDefoptions——选项列表
voidparse_arg_functionvoidconstchar——自定义的解析方法
&&&&constcharopt
&&&&int&optindex&handleoptions1&ret
&&&&prepare_app_argumentsargcargv
&&&&optindex1
&&&&whileoptindex&argc
&&&&&&&&opt&&argvoptindex
&&&&&&&&ifhandleoptions&opt0'-'&opt1'\0'
&&&&&&&&&&&&ifopt1'-'&opt2'\0'
&&&&&&&&&&&&&&&&handleoptions&0
&&&&&&&&&&&&&&&continue
&&&&&&&&&&&&
&&&&&&&&&&&&opt
&&&&&&&&&&&
//eg–acodec copy
&&&&&&&&&&&&ifret&parse_optionoptctx&opt&argvoptindex&options0
&&&&&&&&&&&&&&&exit_program1
&&&&&&&&&&&&optindex&&ret
&&&&&&&&else
&&&&&&&&&&&&ifparse_arg_function
&&&&&&&&&&&&&&&parse_arg_functionoptctx&opt
在此,ffmpeg 默认的处理输出文件名参数为:
staticvoid&opt_output_filevoidoptctxconstcharfilename
2.2处理命令行参数
int&parse_optionvoidoptctxconstcharoptconstchararg&&&&&&&&&&&&&&&&&const&OptionDefoptions
2.2.1查找匹配的Option
&&&&const&OptionDefpo
&&&&int&bool_val1
&&&&intdstcount
&&&&voiddst
&&&&po&&find_optionoptions&opt
&&&&ifponame&opt0'n'&opt1'o'
&&&&&&&&po&&find_optionoptions&opt&2
&&&&&&&&ifponamepoflags&OPT_BOOL
&&&&&&&&&&&&goto&unknown_opt
&&&&&&&&bool_val&0
&&&&ifponame
&&&&&&&&po&&find_optionoptions&default&
&&&&ifponame
unknown_opt
&&&&&&&&av_logNULL&AV_LOG_ERROR&Unrecognizedoption
'%s'\n&&opt
&&&&&&&&return&AVERROREINVAL
&&&&ifpoflags&HAS_ARGarg
&&&&&&&&av_logNULL&AV_LOG_ERROR&Missingargument
for option '%s'\n&&opt
&&&&&&&&return&AVERROREINVAL
现在来查看一下find_option方法的实现:
staticconst&OptionDeffind_optionconst&OptionDefpoconstcharname
根据name在全局变量options数组中查找OptionDef
&&&&constcharp&strchrname':'&
&&&&int&len&p&p&name&strlenname
&&&&whileponameNULL
&&&&&&&&ifstrncmpname&poname&len&strlenponame&len
&&&&&&&&&&&&break
&&&&&&&&po
&&&&return&po
2.2.2寻找选项地址
以下的代码用于将 void*dst变量赋&#20540;。让dst指向需要赋&#20540;的选项地址。
//OptionContext&SpecifierOpt*
&&&&dst&poflagsOPT_OFFSET&OPT_SPEC
uint8_toptctx&pouoff
poudst_ptr
&&&&SpecifierOpt*
&&&&ifpoflags&OPT_SPEC
&&&&&&&&SpecifierOpt&so&dst
&&&&&&&&charp&strchropt':'
&&&&SpecifierOpt *xxx
int&nb_xxx
&&&&&&&&dstcount&intso1
&&&&&&&&so&&grow_arraysosizeofso&dstcountdstcount1
//SpecifierOptspecifier
//specifier
&&&&&&&&sodstcount1specifier&av_strdupp&p1&&
//dstSpecifierOpt
//dstcount
&&&&&&&&dst&sodstcount1u
&&&&av_logNULL&AV_LOG_ERROR&parse_option-&'%s'
'%s' %d %d %d\n&&opt&arg
&&&&&&&&poflags&OPT_SPEC
&&&&&&&&poflags&OPT_STRING
&&&&&&&&poufunc_arg10
在此做出一些说明:
&&&&dst&poflagsOPT_OFFSET&OPT_SPEC
uint8_toptctx&pouoff
poudst_ptr
关于poudst_ptr的指向,在ffmpeg中都是用来设置全局变量使用。如以下代码:
staticint&exit_on_error0
staticconst&OptionDef options
&&&&&xerror&&OPT_BOOLvoidexit_on_error&exit
on error&&error&
poudst_ptr&(voidexit_on_error)
所以之后的对poudst_ptr赋&#20540;也就是对avconv.c中定义的全局变量赋&#20540;。
关于so&grow_arraysosizeofso&dstcountdstcount1
用户数组动态增长方法签名如下:
voidgrow_arrayvoidarrayint&elem_sizeintsizeint&new_size
其内在处理逻辑如下:
&&&&&&&&uint8_t&tmp&av_reallocarray&new_sizeelem_size
&&&&&&&&memsettmpsizeelem_size0new_sizesize&elem_size
&&&&&&&&size&&new_size
&&&&&&&&return&tmp
需要注意到的是int&elem_size在当前的上下文中指的是sizeofso==sizeofSpecifierOpt
2.2.3选项赋&#20540;
&&&&&& 在获得需要赋&#20540;的变量地址之后,接下来的代码流程用于赋&#20540;处理,主要是根据变量类型进行赋&#20540;:
&&&&ifpoflags&OPT_STRING
&&&&&&&&charstr
&&&&&&&&str&&av_strduparg
&&&&&&&&chardst&str
&&&&elseifpoflags&OPT_BOOL
&&&&&&&&intdst&bool_val
&&&&elseifpoflags&OPT_INT
&&&&&&&&intdst&parse_number_or_dieopt&arg&OPT_INT64&INT_MIN&INT_MAX
&&&&elseifpoflags&OPT_INT64
&&&&&&&&int64_tdst&parse_number_or_dieopt&arg&OPT_INT64&INT64_MIN&INT64_MAX
&&&&elseifpoflags&OPT_TIME
&&&&&&&&int64_tdst&parse_time_or_dieopt&arg1
&&&&elseifpoflags&OPT_FLOAT
&&&&&&&&floatdst&parse_number_or_dieopt&arg&OPT_FLOATINFINITY&INFINITY
&&&&elseifpoflags&OPT_DOUBLE
&&&&&&&&doubledst&parse_number_or_dieopt&arg&OPT_DOUBLEINFINITY&INFINITY
&&&&elseifpoufunc_arg
&&&&&&&&int&ret&&poflags&OPT_FUNC2&poufunc2_argoptctx&opt&arg
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&poufunc_argopt&arg
&&&&&&&&ifret0
&&&&&&&&&&&&av_logNULL&AV_LOG_ERROR&Failed
to set value '%s' for option '%s'\n&&arg&opt
&&&&&&&&&&&&return&ret
&&&&ifpoflags&OPT_EXIT
&&&&&&&&exit_program0
&&&&returnpoflags&HAS_ARG
最后对ifpoufunc_arg的方法调用再次说明:
如acodec选项定义:
&acodec&&HAS_ARG&OPT_AUDIO&OPT_FUNC2voidopt_audio_codec&force
audio codec ('copy' to copy stream)&&codec&
我们可以看到,在全局变量options中注册的解析方法为:opt_audio_codec。
现在回到文档开头提到的
avconv&&input_file.mp4&-copy&-copy&-h264_mp4toannexb&–00:00:00&–00:00:10&output_file.ts
的解析上来。下面,将以比较特殊的 -&copy&说明。
首先,在全局变量options中定义了acodec选项的相关信息:
&acodec&&HAS_ARG&OPT_AUDIO&OPT_FUNC2voidopt_audio_codec&force
audio codec ('copy' to copy stream)&&codec&
可以看到:此选项:
1.&&&&&&有参数需要传入
2.&&&&&&处理的是音频数据
3.&&&&&&解析方式是自定义方法
4.&&&&&&解析方法为:&opt_audio_codec
5.&&&&&&其功能是:&forceaudio codec ('copy' to copy stream)&
6.&&&&&&其对应的命令行名称为codec
因此,在parse_option的调用中,对于acodec选项,将用opt_audio_codec解析方法进行处理。
opt_audio_codecoptctx“acodec”“copy”
方法代码如下:
staticint&opt_audio_codecOptionsContextoconstcharoptconstchararg
&&&&return&parse_optiono&codec:a&&arg&options
可以看到,在这里,没有做更多的工作,只是对命令行选项acodec进行了一个转换,使用的解析器进行重新解析:
opt_audio_codecoptctx“codec:a”“copy”
这里需要回顾一下方法
staticconst&OptionDeffind_optionconst&OptionDefpoconstcharname
此方法是在查找name为“codec:a”&的option 时,实际是寻找的&“codec”
&codec&&HAS_ARG&OPT_STRING&OPT_SPECoff&OFFSETcodec_names&codec
name&&codec&
可以看到:此选项:
1.&&&&&&有参数需要传入
2.&&&&&&处理的是类型的数组(SpecifierOpt*)
3.&&&&&&SpecifierOpt&结构体存储的是(char
4.&&&&&&赋&#20540;方式是直接赋&#20540;,偏移位是:off&OFFSETcodec_names,亦即:
typedefstruct&OptionsContext
&&&&/* input/output options */
&&&int64_t start_time
&&&&constcharformat
&&&&SpecifierOptcodec_names
&&&&int&&&&&&&nb_codec_names
5.&&&&&&&其功能是:&codec name&
6.&&&&&&其对应的命令行名称为codec
因此,在调用
parse_optiono&codec:a&&copy&,options
之后,获得的结果是:
typedefstruct&SpecifierOpt
&&&&charspecifier
&&&&&&&&uint8_t&str
&&&&&&&&int&&&&&&&i
&&&&&&&&int64_t&i64
&&&&&&&&float&&&&&&f
&&&&&&&&double&&&dbl
&SpecifierOpt
而在OptionsContext中,
typedefstruct&OptionsContext
&&&&/* input/output options */
&&&&int64_t start_time
&&&&constcharformat
&&&&SpecifierOptcodec_names
&&&&int&&&&&&&nb_codec_names
通过本篇的分析,基本可以明了ffmpeg在输入参数的解析流程。这对我们之后想要把命令行
avconv&&input_file.mp4&-copy&-copy&-h264_mp4toannexb&–00:00:00&–00:00:10&output_file.ts
转换为可用的内嵌代码提供了一个很好的入口和分析点。
在之后的章节中,我们会从此全面进入avconv.c 的世界。
同时,需要指出的是,本章节没有描述两个重要的解析部分:
staticconst&OptionDef options
&&&&&i&&HAS_ARG&OPT_FUNC2voidopt_input_file&input
file name&&filename&
parse_optionso&argc&argv&optionsopt_output_file
这里涉及到的两个解析方法为:
staticint&opt_input_fileOptionsContextoconstcharoptconstcharfilename
staticvoid&opt_output_filevoidoptctxconstcharfilename
这两个方法除了进行Option设置之外,还对输入输出的对应结构和变量进行了初始化,其功能和重要性已经超出了简单的命令行解析的范围,因此,将在后继章节中分析。
本文已收录于以下专栏:
相关文章推荐
之前做的ffmpeg+ffserver实现http流媒体播放,现在做的是ffmpeg+nginx搭建HLS流媒体服务器。
由于做的是基于ARM上的,首先要做nginx的移植,ffmpeg移植网上很多...
HLS也就是HTTP Live Streaming,是苹果出的一个基于HTTP的流媒体通信协议。字面意思有个live,也就是直播相关的。下面介绍如何在windows系统下用ffmpeg+nginx+V...
利用ffmpeg工具基于nginx-rtmp-module模块搭建HLS
目的:使Nginx支持Rtmp协议推流,并支持hls分发功能及FFMPEG转码多码率功能。
一、准备工作
模块:nginx-rtmp-module-master(支持rtmp协议)
下载地址...
简介:HTTP Live Streaming(缩写是 HLS)是一个由苹果公司提出的基于HTTP的流媒体 网络传输协议。
是苹果公司QuickTime X和iPhone软件系统的一部分。它的工作原理...
碰到了如下问题,记录如下:
一、matroska需要libbz2.so
&_BZ2_bzDecompress&, referenced from: _matroska_decode_buffer ...
前前后后搭建了两三个星期,终于可以告一段落,nginx实在是有点强大。写一篇笔记来记录一下这个过程中的思路和解决方案。
一.搭建nginx平台:
基本是基于http://blog.csdn.net/x...
我的NGINrtmp流服务器的简单配置
一.HLS介绍
HLS,Http&#160;Live&#160;Streaming&#160;是由Apple公司定义的用于实时流传输的协议,HLS基于HTTP协议实现,传输内容包括两部分,一是M3U8描述文件,二是TS媒体文件。...
他的最新文章
讲师:姜飞俊
讲师:汪木铃
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)}

我要回帖

更多关于 nginx ffmpeg 不工作 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信