nginx flv模块的使用和源码分析

这里写自定义目录标题

    • 1. 为什么需要ngx_http_flv_module
    • 2. 配置指令
    • 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?start=12345" > /dev/null
当然,最好还是用支持flv流媒体播放的播放器,譬如vlc,ffplay来进行播放测试验证。
但是vlc和ffplay无法通过http flv来进行视频拖拽播放,必须实现一个能够支持http flv拖拽功能的播放器,是需要解析flv文件内容,根据metadata知道每一个关键帧的起始未知,然后在拖拽的时候自动对齐到最近的一个关键帧,然后发起一个新的带有start=xxx的http flv播放地址参数的请求进行播放。

6. 思考

  其实FLV流媒体实现的代码还是非常简单的。如果希望能够像[nginx slice模块的使用和源码分析](https://editor.csdn.net/md/?articleId=136029381)中提到的那样,对FLV进行切片处理,来实现对cdn的缓存友好性,那怎么来做呢?由于FLV流媒体下载和普通的Range请求下载还是有一定的区别的,肯定需要进行特殊的处理。如果有时间的话我再来参数一下支持FLV切片回源的流媒体播放功能吧,敬请期待。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/667764.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Redis之基础篇

Redis简介 Redis是一种基于键值对(Key-Value)的NoSQL数据库,它支持string(字符串)、hash(哈希)、list(列表)、set(集合)、zset(有序集…

Java后端面试总结

Java语言具有哪些特点? Java为纯面向对象的语言。它能够直接反应现实生活中的对象。 具有平台无关性。java利用Java虚拟机运行字节码,无论是在Windows、Linux还是MacOS等其它平台对Java程序进行编译,编译后的程序可在其它平台运行。 Java为解释型语言,编译器把Java代码编译…

随机图论基础

一,随机图、随机图空间 1,随机图 一个n个点的无向图,最多有sn(n-1)/2条边。 每条边都有一定的概率存在,有一定概率不存在,那么每个图都有一个出现概率。 2,随机图空间 一共有2^s种不同的图&#xff0c…

[word] word怎么让字在横线上居中 #学习方法#媒体#媒体

word怎么让字在横线上居中 word怎么让字在横线上居中? 在word文档中,有时为了某些字句突出显示,需要用下划线来表示,当着重显示部分为金额、面积或某些选项时,需要一条长下划线上左右有空白,而文字在下划…

Linux下对线程的理解(上)

1、线程的概念 要理解线程首先要理解页表和进程地址空间,我是这样子理解的,1、进程地址空间是进程访问资源的窗口。2、页表是规定进程地址空间中哪些属于进程。3、合理的使用进程地址空间页表可以对资源进行划分。而如何理解进程呢?进程是接受…

算法题目题单——图论

简介 本文为自己做的一部分图论题目,作为题单列出,持续更新。 题单由题目链接和题解两部分组成,题解部分提供简洁题意,代码仓库:Kaiser-Yang/OJProblems。 对于同一个一级标题下的题目,题目难度尽可能做…

【vscode】windows11在vscode终端控制台中打印console.log()出现中文乱码问题解决

1. 问题描述 在前端开发过程中使用vscode编写node.js,需要在控制台中打印一些中文信息,但是一直出现中文乱码问题,英文和数字都显示正常。在网上试了很多设置的办法,最终找到windos11设置中解决。 2. 原因 首先打开控制台&…

centos 7.7 离线安装docker

centos 7.7 离线安装docker Index of linux/static/stable/x86_64/https://download.docker.com/linux/static/stable/x86_64/ 【1】离线下载docker 压缩包上传至 /usr/local 目录,解压缩,并复制到 /usr/bin/ 目录中。 cd /usr/local/tar -zxvf docke…

力扣 第 123 场双周赛 解题报告 | 珂学家 | 二维偏序+单调队列优化

前言 执手看歌敲金钗,笑语落珠明眸睐。 忽然蝴蝶春风满,焉教冷镜瘦朱颜。 整体评价 T3是基于map的前缀和的变形题,T4是二维偏序的一道应用题。 题外话,力扣还是实现N久之前的承诺了,命名权奖励,赞一个。 …

7隐藏进程_Linux_Rootkit.md

Xcellerator 密码学Linux其他逆向工程 文章目录 [Linux Rootkit 第 7 部分:隐藏进程](https://xcellerator.github.io/posts/linux_rootkits_07/)选择要隐藏的 PID隐藏 PID Linux Rootkit 第 7 部分:隐藏进程 2020-10-01 :: TheXcellerator # linux #…

Python调用pyspark报错整理

前言 Pycharm配置了SSH服务器和Anaconda的python解释器,如果没有配置可参考 大数据单机学习环境搭建(8)Linux单节点Anaconda安装和Pycharm连接 Pycharm执行的脚本 执行如下 pyspark_model.py 的python脚本,构建SparkSession来执行sparksql "&qu…

学习的数据结构和算法第2天

数据结构 ​ 是计算机储存、组织数据的方式,指相互之间存在一种或多种特定关系的数据元素 利用一些方式实现一些项目在内存中奖数据存储起来。 1、数组 2、链表 3、树 算法 ​ 就是定义良好的计算过程,它取一个或一组的值为输入,并…

Quartus IP 之mif与hex文件创建与使用

一、mif与hex概述 ROM IP的数据需要满足断电不丢失的要求,ROM IP数据的文件格式一般有三种文件格式:.mif、.hex、.coe,Xilinx与Intel Altera支持的ROM IP数据文件格式如下: Xilinx与Altera支持的ROM文件格式 Alterahex、mifAM&am…

pwn学习笔记(2)

pwn学习笔记(2) 1.三种常见的寄存器: ​ ax寄存器:通用寄存器,可用于存放多种数据 ​ bp寄存器:存放的是栈帧的栈底地址 ​ sp寄存器:存放的是栈顶的地址 2.栈帧与栈工作的简介&#xff1a…

Objective-C 中的SEL

在 Objective-C 中&#xff0c;SEL&#xff08;Selector&#xff09;是一种用来表示方法的类型。 它实际上是一个指向方法的指针&#xff0c;用于在运行时动态调用方法。 下面是一个使用 SEL 的代码示例&#xff1a; #import <Foundation/Foundation.h>interface MyCl…

SplitFed: When Federated Learning Meets Split Learning

论文链接&#xff1a; [2004.12088v1] SplitFed: When Federated Learning Meets Split Learning (arxiv.org) AAAI 2022 摘要&#xff1a; 联邦学习分割学习&#xff1a;消除其固有的缺点的两种方法&#xff0c;以及结合差分隐私和PixelDP的精细架构配置&#xff0c;以增强…

CSS transition的一些见解

CSSTransition是CSS3中新添加的特性&#xff0c;可以用来创建元素的过渡效果。当元素在大小、颜色、布局、透明度等数值改变时&#xff0c;可以使其产生过渡的动画效果。 CSSTransition的主要属性包括&#xff1a; transition-property&#xff1a;指定当元素哪个属性改变时执…

【Spring连载】使用Spring Data访问Redis(十)----Lua脚本支持Scripting

【Spring连载】使用Spring Data访问Redis&#xff08;十&#xff09;----Lua脚本支持Scripting Redis 2.6及更高版本支持通过eval 和 evalsha 命令运行Lua脚本。Spring Data Redis为运行处理序列化的脚本提供了高级抽象&#xff0c;并自动使用Redis脚本缓存。 脚本可以通过调用…

云计算关键技术

目录 一、云计算关键技术概述 1.1 概述 二、关键技术内容 2.1 虚拟化技术 2.2 分布式数据存储技术 2.3 资源管理技术 2.4 云计算平台管理技术 2.5 多租户隔离技术 2.5.1 多租户技术下SaaS 特征 2.5.2 多租户技术面临的技术难题 2.5.2.1 数据隔离 2.5.2.2 客户化配置…

Can调试工具源码

Can调试工具源码 CANTool通用测试软件是一个专门用来socket can接口进行测试的软件工具&#xff0c;此软件操作简单&#xff0c;容易上手&#xff0c;通过运用此软件可以非常方便的对linux系统的can接口进行测试&#xff0c;从而熟悉can接口的使用方法&#xff0c;并且此为源码…