商贸信息网站,建设有限公司首页,西双版纳 网站建设,wordpress最大上传这里写自定义目录标题 1. 为什么需要ngx_http_flv_module2. 配置指令3. 加载ngx_http_flv_module 模块4. 源码分析4.1 指令分析4.2 ngx_http_flv_handler处理函数 5. 如何请求flv进行验证6. 思考 1. 为什么需要ngx_http_flv_module 毋庸多说#xff0c;就是为了提供在线的http… 这里写自定义目录标题 1. 为什么需要ngx_http_flv_module2. 配置指令3. 加载ngx_http_flv_module 模块4. 源码分析4.1 指令分析4.2 ngx_http_flv_handler处理函数 5. 如何请求flv进行验证6. 思考 1. 为什么需要ngx_http_flv_module 毋庸多说就是为了提供在线的http flv流媒体播放服务。在若干年前adobe flash player风靡的年代可以说http flv是最流行的在线流媒体点播的解决方案甚至很多直播也用http flv来实现可以和RTMP实时流媒体协议的实时性相当。可是若干年过去了随着H5的兴起新的视频编码器的出现adobe flash player已经被各大浏览器彻底抛弃了虽然http flv也逐渐式微了但是因为flv格式有着格式简单高效的特点很容易进行流化处理所以用http flv协议进行流媒体播放还一直生生不息。那么如何用nginx搭建一个http flv的流媒体服务器呢这里就要用到ngx_http_flv_module了。2. 配置指令 ngx_http_flv_module 的配置非常方便。只要在nginx.conf的location块中添加以下指令flv即可开启flv流媒体模块。3. 加载ngx_http_flv_module 模块 在configure的时候需要添加ngx_http_flv_module来将其编译进来使用如下命令./configure --with-http_flv_module然后在nginx.conf 中添加以下配置如location / {flv;root html;
}4. 源码分析
4.1 指令分析
static ngx_command_t ngx_http_flv_commands[] {{ ngx_string(flv),NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,ngx_http_flv,0,0,NULL },ngx_null_command
};从以上代码知道flv指令在location中使用后面不带参数并且用ngx_http_flv指令分析函数进行解析。
下面来看看ngx_http_flv函数如下static char *
ngx_http_flv(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{ngx_http_core_loc_conf_t *clcf;clcf ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);clcf-handler ngx_http_flv_handler;return NGX_CONF_OK;
}代码非常简单就是在nginx 的http content phase阶段将处理回调函数挂进去这里挂进去的钩子函数即ngx_http_flv_handler。将回调函数挂到content phase有两种方法不过大都采用本模块使用的方法另外也可以王ngx_htt_core_main_conf_t全局结构体的phases[NGX_HTTP_CONTENT_PHASE]动态数组添加回调函数来实现。4.2 ngx_http_flv_handler处理函数 当用户请求的url匹配到nginx.conf中的某个开启了flv的location后在content phase阶段就会触发调用ngx_http_flv_handler函数。下面对该函数的实现过程进行详细解析static ngx_int_t
ngx_http_flv_handler(ngx_http_request_t *r)
{u_char *last;off_t start, len;size_t root;ngx_int_t rc;ngx_uint_t level, i;ngx_str_t path, value;ngx_log_t *log;ngx_buf_t *b;ngx_chain_t out[2];ngx_open_file_info_t of;ngx_http_core_loc_conf_t *clcf;/* 仅支持HTTP GET/HEAD方法否则返回http 405错误码 */if (!(r-method (NGX_HTTP_GET|NGX_HTTP_HEAD))) {return NGX_HTTP_NOT_ALLOWED;}/* 请求地址不能以/结尾意味着没有指定特定的文件则返回NGX_DECLINED,即跳过本模块进行处理 */if (r-uri.data[r-uri.len - 1] /) {return NGX_DECLINED;}/* 因为是流媒体服务没有需要处理HTTP请求的body部分所以需要丢弃请求的body内容 */rc ngx_http_discard_request_body(r);if (rc ! NGX_OK) {return rc;}/* 将请求的url地址映射到本地硬盘上的文件路径, 其中path为返回值即映射出来的本地硬盘上的文件路径其中root为返回值即当前location对应的根目录的字符串长度返回值是指向path字符串的结尾符\0的指针 */last ngx_http_map_uri_to_path(r, path, root, 0);if (last NULL) {return NGX_HTTP_INTERNAL_SERVER_ERROR;}log r-connection-log;path.len last - path.data;ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,http flv filename: \%V\, path);clcf ngx_http_get_module_loc_conf(r, ngx_http_core_module);/* 以下准备打开flv文件 */ngx_memzero(of, sizeof(ngx_open_file_info_t));of.read_ahead clcf-read_ahead;of.directio clcf-directio;of.valid clcf-open_file_cache_valid;of.min_uses clcf-open_file_cache_min_uses;of.errors clcf-open_file_cache_errors;of.events clcf-open_file_cache_events;if (ngx_http_set_disable_symlinks(r, clcf, path, of) ! NGX_OK) {return NGX_HTTP_INTERNAL_SERVER_ERROR;}/* 在nginx的文件缓冲区中打开flv文件 */if (ngx_open_cached_file(clcf-open_file_cache, path, of, r-pool)! NGX_OK){ /* 以下为打开失败的处理逻辑 */switch (of.err) {case 0:return NGX_HTTP_INTERNAL_SERVER_ERROR;case NGX_ENOENT:case NGX_ENOTDIR:case NGX_ENAMETOOLONG:level NGX_LOG_ERR;rc NGX_HTTP_NOT_FOUND;break;case NGX_EACCES:
#if (NGX_HAVE_OPENAT)case NGX_EMLINK:case NGX_ELOOP:
#endiflevel NGX_LOG_ERR;rc NGX_HTTP_FORBIDDEN;break;default:level NGX_LOG_CRIT;rc NGX_HTTP_INTERNAL_SERVER_ERROR;break;}if (rc ! NGX_HTTP_NOT_FOUND || clcf-log_not_found) {ngx_log_error(level, log, of.err,%s \%s\ failed, of.failed, path.data);}return rc;}/* 如果打开的不是文件那么久关闭文件句柄并返回NGX_DECLINED */if (!of.is_file) {if (ngx_close_file(of.fd) NGX_FILE_ERROR) {ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,ngx_close_file_n \%s\ failed, path.data);}return NGX_DECLINED;}r-root_tested !r-error_page;start 0;len of.size;i 1;/* 根据请求url中的start来获取客户端实际想要的视频内容开始的文件偏移量 */if (r-args.len) {if (ngx_http_arg(r, (u_char *) start, 5, value) NGX_OK) {start ngx_atoof(value.data, value.len);if (start NGX_ERROR || start len) {start 0;}/* 如果不是从头开始请求那么响应的内容长度按照以下代码进行计算if (start) {len sizeof(ngx_flv_header) - 1 len - start;i 0; /* i 0 表示不是响应完整内容 */}}}log-action sending flv to client;/* 响应的状态设置为200 Content-Length设置为将要响应的文件的长度 */r-headers_out.status NGX_HTTP_OK;r-headers_out.content_length_n len;r-headers_out.last_modified_time of.mtime;/* 设置当前文件的http etag header值 */if (ngx_http_set_etag(r) ! NGX_OK) {return NGX_HTTP_INTERNAL_SERVER_ERROR;}/* 设置文件的Content-Type类型 */if (ngx_http_set_content_type(r) ! NGX_OK) {return NGX_HTTP_INTERNAL_SERVER_ERROR;}/* 不是响应完整文件内容的情况需要自己构建一个flv header放入响应缓冲区中 */if (i 0) { b ngx_calloc_buf(r-pool);if (b NULL) {return NGX_HTTP_INTERNAL_SERVER_ERROR;}b-pos ngx_flv_header;b-last ngx_flv_header sizeof(ngx_flv_header) - 1;b-memory 1;out[0].buf b;out[0].next out[1]; /* 将flv头部缓存连接到flv body缓存,构成一个响应缓存链 */}/* 接下去分配一个文件类型的ngx_buf_t缓冲区 */b ngx_calloc_buf(r-pool);if (b NULL) {return NGX_HTTP_INTERNAL_SERVER_ERROR;}b-file ngx_pcalloc(r-pool, sizeof(ngx_file_t));if (b-file NULL) {return NGX_HTTP_INTERNAL_SERVER_ERROR;}/* 设置允许ngx_http_range_filter_module处理Range请求 */r-allow_ranges 1;/* 发送http响应头 */rc ngx_http_send_header(r);if (rc NGX_ERROR || rc NGX_OK || r-header_only) {return rc;}/* 设置文件缓存的起始结束便宜位置 */b-file_pos start;b-file_last of.size;/* 设置ngx_buf_t的in_file标记表示是文件缓存 */b-in_file b-file_last ? 1: 0;b-last_buf (r r-main) ? 1 : 0; /* 如果是主请求设置当前ngx_buf_t是最后一个buf */b-last_in_chain 1; /* 设置为当前ngx_buf_t是响应缓存链中的最后一个 */b-file-fd of.fd; /* 设置文件句柄 */b-file-name path; /* 设置文件名 */b-file-log log;b-file-directio of.is_directio;out[1].buf b;out[1].next NULL; /* 设置next为NULL, 表示当前是最后一个ngx_buf_t *//* 通知nginx框架发送flv内容 */return ngx_http_output_filter(r, out[i]);
}5. 如何请求flv进行验证
可以通过以下命令行例如curl http://127.0.0.1:8080/path/test.flv?start12345 /dev/null当然最好还是用支持flv流媒体播放的播放器譬如vlcffplay来进行播放测试验证。
但是vlc和ffplay无法通过http flv来进行视频拖拽播放必须实现一个能够支持http flv拖拽功能的播放器是需要解析flv文件内容根据metadata知道每一个关键帧的起始未知然后在拖拽的时候自动对齐到最近的一个关键帧然后发起一个新的带有startxxx的http flv播放地址参数的请求进行播放。6. 思考 其实FLV流媒体实现的代码还是非常简单的。如果希望能够像[nginx slice模块的使用和源码分析](https://editor.csdn.net/md/?articleId136029381)中提到的那样对FLV进行切片处理来实现对cdn的缓存友好性那怎么来做呢由于FLV流媒体下载和普通的Range请求下载还是有一定的区别的肯定需要进行特殊的处理。如果有时间的话我再来参数一下支持FLV切片回源的流媒体播放功能吧敬请期待。