深入理解nginx mp4流媒体模块[上]
目录
- 1. 引言
- 2. 配置
- 3. 源码分析
- 3.1 配置指令
- 3.1.1 mp4
- 3.1.2 mp4_buffer_size
- 3.1.3 mp4_max_buffer_size
- 3.1.4 mp4_start_key_frame
- 3.2 MP4的请求处理过程
- 3.2.1 预处理
- 3.2.2 找到并打开本地mp4文件
- 3.2.3 解析请求参数
- 3.2.4 MP4文件的处理
深入理解nginx mp4流媒体模块[上]
深入理解nginx mp4流媒体模块[中]
深入理解nginx mp4流媒体模块[下]
1. 引言
在当今数字化时代,视频已成为互联网上最主要的内容形式之一。NGINX作为一款高性能的Web服务器和反向代理服务器,提供了强大的MP4模块,用于优化MP4视频的点播传输功能,并支持播放器的任意拖拽功能。本文将通过通过源码分析深入探讨NGINX MP4模块的实现源码,介绍其功能和实现原理。
NGINX MP4模块的作用和优势
NGINX MP4模块的主要作用是优化MP4视频的点播传输功能,提供快速启动和流畅播放的体验。它通过减少客户端和Web服务器之间的交互,降低额外数据消耗,显著减少流媒体播放的启动时间。以下是NGINX MP4模块的优势:
- 快速启动时间:通过预读取视频文件的元数据,NGINX MP4模块实现了快速的启动时间。用户请求播放视频时,只需加载视频的元数据,无需等待整个视频文件加载完毕。
- 支持任意拖拽功能:现代浏览器在Web服务器支持HTTP Range请求的情况下,可以通过MP4模块实现视频的任意拖拽功能,提供更好的用户体验。
- 减少数据传输:MP4模块减少了不必要的HTTP请求,通过边播边加载的方式为用户提供视频流,减少额外的性能消耗。
NGINX MP4模块的实现原理
NGINX MP4模块通过读取和解析MP4视频文件的元数据,实现优化的点播传输。它预读取视频文件的元数据,包括视频的时长、编码信息、音频信息等,并将这些信息缓存到内存中。当用户请求播放视频时,NGINX MP4模块直接从内存中获取元数据,根据客户端的请求,按需传输视频片段,实现快速启动和流畅播放的效果。
2. 配置
要使用NGINX MP4模块,需要在NGINX的配置文件中进行相应的配置。以下是一个简单的配置示例:
location /videos/ {root html;mp4; # 开启mp4流媒体功能mp4_buffer_size 1m; # mp4 moov元数据缓存的默认空间大小mp4_max_buffer_size 10m; # mp4 moov元数据缓存的最大空间
}
通过以上配置,就可以通过 curl模拟播放器访问了。例如:
#从头开始播放
curl "http://127.0.0.1/videos/test.mp4" #从第100s播放到200s
curl "http://127.0.0.1/videos/test.mp4?start=100&end=200"
这里需要强调的是,对于一些特别大的mp4文件,可能moov元数据的大小就超过了mp4_max_buffer_size,会导致nginx报错的情况,但是如果设置太大,特别是mp4_buffer_size设置得太大,就会使得nginx消耗太多的内存,引起其他问题。因此,需要预先对moov大小有一个预估。
3. 源码分析
3.1 配置指令
3.1.1 mp4
{ ngx_string("mp4"),NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,ngx_http_mp4,0,0,NULL },
&emps;这个指令开启mp4流媒体功能,从以上定义可以知道这个指令只能在location中配置。
在ngx_http_mp4配置指令解析函数中,设置了ngx_http_mp4_handler回调函数,如下:
static char *
ngx_http_mp4(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_mp4_handler;return NGX_CONF_OK;
}
该回调函数会在NGX_HTTP_CONTENT_PHRASE阶段回调这个函数进行mp4的处理。
3.1.2 mp4_buffer_size
{ ngx_string("mp4_buffer_size"),NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,ngx_conf_set_size_slot,NGX_HTTP_LOC_CONF_OFFSET,offsetof(ngx_http_mp4_conf_t, buffer_size),NULL },
这个指令定义了moov数据缓冲区的默认大小,可以在http/server/location中配置。
3.1.3 mp4_max_buffer_size
{ ngx_string("mp4_max_buffer_size"),NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,ngx_conf_set_size_slot,NGX_HTTP_LOC_CONF_OFFSET,offsetof(ngx_http_mp4_conf_t, max_buffer_size),NULL },
这个指令定义了moov数据缓冲区的最大空间,可以在http/server/location中配置。
3.1.4 mp4_start_key_frame
{ ngx_string("mp4_start_key_frame"),NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,ngx_conf_set_flag_slot,NGX_HTTP_LOC_CONF_OFFSET,offsetof(ngx_http_mp4_conf_t, start_key_frame),NULL },
这个指令设置是否将视频起始帧对齐到最近的关键帧开始发送数据。
3.2 MP4的请求处理过程
下面以ngx_http_mp4_handler函数为分析对象,说明MP4的请求处理过程。
3.2.1 预处理
- 过滤非GET/HEAD请求。
if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {return NGX_HTTP_NOT_ALLOWED;
}
- 取消接收客户端请求的http body部分。
rc = ngx_http_discard_request_body(r);
3.2.2 找到并打开本地mp4文件
- 获取mp4文件的完整路径
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;
- 打开mp4文件
of.read_ahead = clcf->read_ahead;
of.directio = NGX_MAX_OFF_T_VALUE;
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;/*用于设置NGINX服务器是否允许访问符号链接文件的功能。当启用该功能时,NGINX将拒绝通过符号链接文件访问文件系统中的文件。
*/
if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {return NGX_HTTP_INTERNAL_SERVER_ERROR;
}if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)!= NGX_OK)
{
......
}
3.2.3 解析请求参数
从http请求的querystring部分提取到start和end参数,这两个参数的单位都是秒。
if (r->args.len) {if (ngx_http_arg(r, (u_char *) "start", 5, &value) == NGX_OK) {/** A Flash player may send start value with a lot of digits* after dot so a custom function is used instead of ngx_atofp().*/start = ngx_http_mp4_atofp(value.data, value.len, 3);}if (ngx_http_arg(r, (u_char *) "end", 3, &value) == NGX_OK) {end = ngx_http_mp4_atofp(value.data, value.len, 3);if (end > 0) {if (start < 0) {start = 0;}if (end > start) {length = end - start;}}}
}
3.2.4 MP4文件的处理
if (start >= 0) {r->single_range = 1;/* 分配并初始化mp4处理上下文 */mp4 = ngx_pcalloc(r->pool, sizeof(ngx_http_mp4_file_t));if (mp4 == NULL) {return NGX_HTTP_INTERNAL_SERVER_ERROR;}mp4->file.fd = of.fd;mp4->file.name = path;mp4->file.log = r->connection->log;mp4->end = of.size;mp4->start = (ngx_uint_t) start;mp4->length = length;mp4->request = r;/* 加载并调整mp4的moov元信息帧索引 */switch (ngx_http_mp4_process(mp4)) {case NGX_DECLINED:if (mp4->buffer) {ngx_pfree(r->pool, mp4->buffer);}ngx_pfree(r->pool, mp4);mp4 = NULL;break;case NGX_OK:r->headers_out.content_length_n = mp4->content_length;break;default: /* NGX_ERROR */if (mp4->buffer) {ngx_pfree(r->pool, mp4->buffer);}ngx_pfree(r->pool, mp4);return NGX_HTTP_INTERNAL_SERVER_ERROR;}
}
以下对ngx_http_mp4_file_t的结构定义进行说明:
typedef struct {ngx_file_t file; # mp4文件对象u_char *buffer; # 用于mp4分析的缓冲区u_char *buffer_start; # buffer空闲的起始位置u_char *buffer_pos; # buffer中可用于分析的起始位置u_char *buffer_end; # buffer中可用于分析的结束位置size_t buffer_size; # mp4分析缓冲区buffer的大小off_t offset; # 当前mp4文件读取的偏移量off_t end; # 当前mp4文件的文件大小off_t content_length; # 最终发送给客户端响应的内容长度ngx_uint_t start; # 请求的起始偏移时间ngx_uint_t length; # 请求的视频时长uint32_t timescale; # mp4文件中设置的时间scale值ngx_http_request_t *request; # 对应当前的http request对象ngx_array_t trak; # mp4包含的track列表,引用traks,最多2个ngx_http_mp4_trak_t traks[2]; # mp4包含的track列表size_t ftyp_size; # ftyp atom的大小size_t moov_size; # moov atom的大小ngx_chain_t *out;ngx_chain_t ftyp_atom; # 链接了ftyp_atom_buf的缓冲区链ngx_chain_t moov_atom; # 链接了moov_atom_buf的缓冲区链ngx_chain_t mvhd_atom; # 链接了mvhd_atom_buf的缓冲区链ngx_chain_t mdat_atom; # 链接了mdat_atom_buf的缓冲区链ngx_chain_t mdat_data; # 链接了mdat_data_buf的缓冲区链ngx_buf_t ftyp_atom_buf; # ftyp atom的缓冲区ngx_buf_t moov_atom_buf; # moov atom的缓冲区ngx_buf_t mvhd_atom_buf; # mvhd atom的缓冲区ngx_buf_t mdat_atom_buf; # mdat atom的缓冲区ngx_buf_t mdat_data_buf; # mdat atom的缓冲区u_char moov_atom_header[8];u_char mdat_atom_header[16];
} ngx_http_mp4_file_t;
<未完待续>
下接:深入理解nginx mp4流媒体模块[中]
相关文章:
深入理解nginx mp4流媒体模块[上]
目录 1. 引言2. 配置3. 源码分析3.1 配置指令3.1.1 mp43.1.2 mp4_buffer_size3.1.3 mp4_max_buffer_size3.1.4 mp4_start_key_frame 3.2 MP4的请求处理过程3.2.1 预处理3.2.2 找到并打开本地mp4文件3.2.3 解析请求参数3.2.4 MP4文件的处理 深入理解nginx mp4流媒体模块[上] 深入…...
Go 之 Gin 框架
Gin 是一个 Go (Golang) 编写的轻量级 web 框架,运行速度非常快,擅长 Api 接口的高并发,如果项目的规模不大,业务相对简单,这个时候我们也推荐您使用 Gin,特别适合微服务框架。 简单路由配置 package mai…...
vue3+threejs新手从零开发卡牌游戏(二十一):添加战斗与生命值关联逻辑
首先将双方玩家的HP存入store中,stores/common.ts代码如下: import { ref, computed } from vue import { defineStore } from piniaexport const useCommonStore defineStore(common, () > {const _font ref() // 字体const p1HP ref(4000) // 己…...
Linux内核err.h文件分析
在阅读和编写内核相关的代码时,经常会看到IS_ERR、ERR_PTR等函数。这些函数在内核头文件的err.h中。以我服务器的代码为例,内核版本为5.15。 这个文件的代码如下: /* SPDX-License-Identifier: GPL-2.0 */ #ifndef _LINUX_ERR_H #define _L…...
Qt 富文本处理 (字体颜色大小加粗等)
Qt中支持HTML的控件有textEdit 、label 、textBrowser 。 接口:setHtml("Qt"); toHtml(). 文本样式设置 : 可分字设置 ,主要使用QTextCharFormat类进行文本样式设置。 示例: QTextCharFormat fmt; //粗体 fmt.setFontWeight…...
消息队列的七种经典应用场景
在笔者心中,消息队列,缓存,分库分表是高并发解决方案三剑客。 在职业生涯中,笔者曾经使用过 ActiveMQ 、RabbitMQ 、Kafka 、RocketMQ 这些知名的消息队列 。 这篇文章,笔者结合自己的真实经历,和大家分享…...
uniapp 微信小程序 canvas 手写板文字重复倾斜水印
核心逻辑 先将坐标系中心点通过ctx.translate(canvasw / 2, canvash / 2) 平移到canvas 中心,再旋转设置水印 假如不 translate 直接旋转,则此时的旋转中心为左上角原点,此时旋转示意如图所示 当translate到中心点之后再旋转,此…...
JavaScript如何制作轮播图
在JavaScript中实现轮播图可以通过多种方式,但最常见的方式是使用数组来存储图片,然后使用setInterval函数定期更改显示的图片。下面是一个简单的例子: 首先,你需要在HTML中设置一些用于显示图片的<img>标签,以…...
【面试经典150 | 动态规划】零钱兑换
文章目录 Tag题目来源解题思路方法一:动态规划 写在最后 Tag 【动态规划】【数组】 题目来源 322. 零钱兑换 解题思路 方法一:动态规划 定义状态 dp[i] 表示凑成总金额的最少硬币个数。 状态转移 从小到大枚举要凑成的金额 i,如果当前…...
什么是防火墙,部署防火墙有什么好处?
与我们的房屋没有围墙或界限墙一样,没有防护措施的计算机和网络将容易受到黑客的入侵,这将使我们的网络处于巨大的风险之中。因此,就像围墙保护我们的房屋一样,虚拟墙也可以保护和安全我们的设备,使入侵者无法轻易进入…...
学习鸿蒙基础(10)
目录 一、轮播组件 Swiper 二、列表-List 1、简单的List 2、嵌套的List 三、Tabs容器组件 1、系统自带tabs案例 2、自定义导航栏: 一、轮播组件 Swiper Entry Component struct PageSwiper {State message: string Hello Worldprivate SwCon: SwiperControl…...
阿里云对象存储OSS入门
阅读目录 一、阿里云OSS的使用 1、OSS是什么?2、OSS的使用 二、阿里云OSS的使用三、图床的搭建四:图床绑定阿里云OSS 编写不易,如果我的文章对你有帮助的话,麻烦小伙伴还帮忙点个赞再走! 如果有小伙伴觉得写的啰嗦&a…...
[幻灯片]软件需求设计方法学全程实例剖析-03-业务用例图和业务序列图
DDD领域驱动设计批评文集 做强化自测题获得“软件方法建模师”称号 《软件方法》各章合集 pdf已上传至本号的CSDN资源,或到以下地址下载: http://umlchina.com/training/umlchina_03_bm.pdf...
ctfshow-web入门-xxe
什么是xxe? XXE,全称XML External Entity Injection,即XML外部实体注入。这是一种针对应用程序解析XML输入类型的攻击。当包含对外部实体的引用的XML输入被弱配置的XML解析器处理时,就会发生这种攻击。这种攻击通过构造恶意内容&…...
Docker数据卷挂载
一、容器与数据耦合的问题: 数据卷是虚拟的,不真实存在的,它指向文件中的文件夹 ,属主机文件系统通过数据卷和容器数据进行联系,你改变我也改变。 解决办法: 对宿主机文件系统内的文件进行修改,会立刻反应…...
QT_day4:对话框
1、完善对话框,点击登录对话框,如果账号和密码匹配,则弹出信息对话框,给出提示”登录成功“,提供一个Ok按钮,用户点击Ok后,关闭登录界面,跳转到其他界面 如果账号和密码不匹配&…...
矢量数据库:连接人工智能应用程序的数据复杂性与可用性的桥梁
关注我的公众号:Halo咯咯 简介 矢量数据库是一种专门设计的数据库,专注于高效地存储、管理和操作矢量数据。与传统数据库处理标量值(如数字、字符串、日期)不同,矢量数据库针对的是那些表现为多维数据点的向量…...
docker:can’t create unix socket /var/run/docker.sock: is a directory
docker:can’t create unix socket /var/run/docker.sock: is a directory 原因:docker.sock不能创建 解决方式: rm -rf /var/run/docker.sock 然后重新启动docker Docker是一种相对使用较简单的容器,我们可以通过以下几种方式获取信息&…...
Qt 图形视图 /图形视图框架坐标系统的设计理念和使用方法
文章目录 概述Qt 坐标系统图形视图的渲染过程Item图形项坐标系Scene场景坐标系View视图坐标系map坐标映射场景坐标转项坐标视图坐标转图形项坐标图形项之间的坐标转换 其他 概述 The Graphics View Coordinate System 图形视图坐标系统是Qt图形视图框架的重要组成部分…...
视频号小店类目资质如何申请?新手看一遍就懂了!
我是电商珠珠 大家在视频号小店后台新增商品的时候,需要先完成类目资质的申请,通过后才可以上架相关商品。 而类目资质分为普通类目和特殊类目,如果你所上架的商品属于开放类目,那么就去按照普通类目资质去申请。 如果是定向准…...
别再找插件了!手把手教你用uni-app的Canvas API画一个带渐变和刻度的环形进度条
原生Canvas魔法:在uni-app中打造高性能渐变环形进度条 每次看到那些酷炫的数据可视化图表,你是不是也想过自己动手实现?但面对复杂的第三方图表库文档和性能问题又望而却步。今天我要分享的是如何用uni-app原生Canvas API,从零开始…...
果实采摘机械手的设计【论文+CAD图纸+Creo三维+外文文献翻译】
果实采摘机械手作为现代农业装备领域的重要创新,其核心作用在于解决传统人工采摘效率低、劳动强度大、成本高等问题。通过机械结构与控制系统的协同设计,该设备可模拟人手抓取动作,精准完成果实识别、定位、采摘及收集全流程,显著…...
小白友好!MogFace本地部署全攻略,从安装到检测只需3步
小白友好!MogFace本地部署全攻略,从安装到检测只需3步 1. 工具简介 MogFace是一款基于CVPR 2022论文的高精度人脸检测工具,特别适合需要保护隐私的本地化应用场景。它能够准确识别照片中的多个人脸,无论这些人脸是大是小、是正脸…...
避开原子操作坑!Keil AC5移植LwRB 3.0.0的保姆级避坑指南
避开原子操作坑!Keil AC5移植LwRB 3.0.0的保姆级避坑指南 在嵌入式开发中,环形缓冲区(Ring Buffer)是一种常见的数据结构,广泛应用于串口通信、DMA传输等场景。LwRB(Lightweight Ring Buffer)作…...
别再混淆了!一文搞懂目标检测中的AP、mAP和mAP@0.5:0.95区别
目标检测评估指标全解析:从AP到mAP0.5:0.95的实战指南 在计算机视觉领域,目标检测模型的性能评估一直是研究者关注的焦点。面对AP、mAP、mAP0.5:0.95等专业术语,不少开发者容易混淆它们的计算方式和适用场景。本文将深入剖析这些关键指标的技…...
Multisim课程设计救星:从卡诺图到仿真,手把手搞定五人表决器(附源文件)
五人表决器数字电路设计实战:从卡诺图到Multisim仿真的全流程解析 第一次拿到数字电路课程设计任务书时,看着"五人表决器"这个题目,我的大脑和实验室的示波器一样一片空白。直到在面包板上成功点亮第一个LED指示灯,才真…...
OpenAI推出Safety Bug Bounty计划:聚焦AI滥用与安全风险
OpenAI正式启动公共Safety Bug Bounty(安全漏洞赏金计划),旨在鼓励全球研究人员识别其产品中存在的AI滥用行为和安全风险。该计划托管于Bugcrowd平台,是对现有Security Bug Bounty的重要补充,专门处理那些虽不符合传统…...
手把手教你用JavaScript实现国密SM4加密(附Node.js与微信小程序兼容代码)
从零构建JavaScript国密SM4加密引擎:跨平台实战指南 国密SM4算法作为我国商用密码体系的核心标准,正在金融、政务等领域加速替代国际加密算法。但对于JavaScript开发者而言,直接可用的SM4实现往往面临三大痛点:Node.js与微信小程序…...
用快马平台5分钟构建qoderwork理念下的待办事项应用原型
最近在研究qoderwork这个概念,简单来说就是通过AI辅助快速把想法变成可运行的代码原型。正好用InsCode(快马)平台试了下做个待办事项应用,整个过程比想象中顺畅很多,分享下具体实现思路。 整体框架搭建 首先确定基础HTML结构,分为…...
ESP32-S3 开发实战:从问题排查到功能优化
1. ESP32-S3开发环境搭建与常见问题 刚拿到ESP32-S3开发板时,我最先遇到的就是环境配置问题。这里分享几个新手容易踩的坑:首先是开发工具链的选择,官方推荐使用ESP-IDF或Arduino IDE。我建议初学者先用Arduino IDE上手,因为它的库…...
