FFmpeg Muxer HLS

使用FFmpeg命令来研究它对HLS协议的支持程度是最好的方法:

ffmpeg -h muxer=hls

Muxer HLS

Muxer hls [Apple HTTP Live Streaming]:Common extensions: m3u8.Default video codec: h264.Default audio codec: aac.Default subtitle codec: webvtt.

这里面告诉我们,FFmpeg中的Muxer hls实际上是对于Apple HTTP Live Streaming的一种实现(HLS,全称HTTP Live Streaming,是Apple公司发布的协议),这里明确说明了HLS只是一种封装格式而与编码无关。

默认的文件扩展名为m3u8​,我们在浏览器中观看动漫、电影的时候,可以使用工具去查看它里面的链接。最终,你大概率会发现这样一种m3u8​文件的访问链接。

FFmpeg的hls muxer默认支持的视频、音频和字幕的编码格式分别是:h264​、aac​和webvtt​。这也就意味着如果我们想要对其他编码格式的音频或者视频进行HLS封装,那么就需要显式地去指定这些编码格式。需要注意的是,这些其他的编码格式需要是HLS协议支持的编码格式。

假设我们需要对一个MP4文件进行HLS切片,更准确地说是将MP$的封装格式转换成HLS的封装格式,只不过HLS这个封装格式是由多个音视频文件和一个M3U8(该文件在HLS协议中被称为Media Playlist,用作指导这些切片后的音视频如何播放)组成的。

例子

多的不说,我这里找一个MP4文件,使用FFmpeg将其转换成hls的封装格式,看看会出现什么样的结果。

这里我使用ffprobe查看该MP4文件的编码,来明确是否需要进行转码操作:

Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'test_input.mp4':Metadata:major_brand     : isomminor_version   : 512compatible_brands: isomiso2avc1mp41encoder         : Lavf60.16.100Duration: 00:00:14.82, start: 0.000000, bitrate: 2662 kb/sStream #0:0[0x1](und): Video: h264 (High) (avc1 / 0x31637661), yuv420p(tv, bt709, progressive), 1920x1200 [SAR 1:1 DAR 8:5], 2489 kb/s, 60 fps, 60 tbr, 15360 tbn (default)Metadata:handler_name    : VideoHandlervendor_id       : [0][0][0][0]Stream #0:1[0x2](und): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 159 kb/s (default)Metadata:handler_name    : SoundHandlervendor_id       : [0][0][0][0]

你可以看到,视频编码h264​和音频编码aac​都是默认的,这意味着我们不需要转码。

因此,我们可以使用以下命令来进行转封装:

mkdir output & ffmpeg -i test_input.mp4 -c copy -f hls output/index.m3u8

使用命令来查看output​目录中的内容:

> tree output
output
├── index.m3u8
├── index0.ts
├── index1.ts
├── index2.ts
└── index3.ts> cat output/index.m3u8
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:4
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:4.166667,
index0.ts
#EXTINF:4.166667,
index1.ts
#EXTINF:4.166667,
index2.ts
#EXTINF:2.366667,
index3.ts
#EXT-X-ENDLIST

此刻,output​目录中的所有内容,组成了HLS协议的封装格式,虽然它们由多个文件组成。

但是,当需要切片的文件变大时,index.m3u8​的内容会和实际的切片数量对不上,原因就是:默认的hls_list_size​的值为5​。因此,index.m3u8文件中只会记录最新的5个切片。使用-hls_list_size​能够自己指定这个值。

选项

额外指定一些选项,让HLS的切片符合你的需求。

start_number

设置开始的序列号,默认从0开始。这里我们设置序列号为1进行切片,那么产生的结果为:

> mkdir output & ffmpeg -i test_input.mp4 -c copy -f hls -start_number 1 output/index.m3u8
> tree output
output
├── index.m3u8
├── index1.ts
├── index2.ts
├── index3.ts
└── index4.ts

注意,生成的切片文件名的序列号和m3u8文件中的序列号是对应的。

hls_time

指定切片的时间长度,单位为秒,默认值为2,类型是float​。

> cat output/index.m3u8
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:4
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:4.166667,
index0.ts
#EXTINF:4.166667,
index1.ts
#EXTINF:4.166667,
index2.ts
#EXTINF:2.366667,
index3.ts
#EXT-X-ENDLIST

#EXTINF​就是切片的时间长度,这里切片的时间长度为4.166667​,这和默认值2​不相符。这是什么原因导致的呢?我猜测,这可能是因为HLS对于每个切片的关键帧具有某种要求,导致了最终的切片时间按照原本视频的关键帧分布来进行切片。

使用-force_key_frames "expr:gte(t,n_forced*2)"​来让视频GOP大小为2秒,以此让切片能够按照我们所设置的参数运行:

$ ffmpeg -i test_input.mp4 \ 
> -f hls \ 
> -force_key_frames "expr:gte(t,n_forced*2)" \
> -hls_time 2 \
> output/index.m3u8$ cat output/index.m3u8
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:2
#EXT-X-MEDIA-SEQUENCE:3
#EXTINF:2.000000,
index3.ts
#EXTINF:2.000000,
index4.ts
#EXTINF:2.000000,
index5.ts
#EXTINF:2.000000,
index6.ts
#EXTINF:0.866667,
index7.ts
#EXT-X-ENDLIST

这证明我们的猜测是正确的。注意,由于-force_key_frames​选项改变了原本的视频帧,因此不能够指定-c copy​(如果指定,则会导致我们无法对原本的视频编码做出任何改变,-force_key_frames​就会失效)。

你可以看到上面只记录了5个切片,3 4 5 6 7​,而我们的切片明明是从0开始的,并且0 1 2​确实存在于output目录中,但却没有被m3u8文件记录,这就关系到-hls_list_size​的使用了

hls_list_size

前面说了,由于m3u8​这个Media Playlist只会记录最新的几个切片,这可能会导致播放错误。该选项默认值是5,当切片多于5个时,你就要考虑将其设置地大一些,防止切片错误。

hls_base_url

你可以指定切片的基础路径,比如:http://xxx.com/​。这样浏览器可以通过读取m3u8​文件,然后通过网络来访问这些切片。

比如:

> ffmpeg -i test_input.mp4 -c copy -f hls \
> -hls_base_url "http://www.aderversa.com/" \
> output/index.m3u8> cat output/index.m3u8
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:4
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:4.166667,
http://www.aderversa.com/index0.ts
#EXTINF:4.166667,
http://www.aderversa.com/index1.ts
#EXTINF:4.166667,
http://www.aderversa.com/index2.ts
#EXTINF:2.316667,
http://www.aderversa.com/index3.ts
#EXT-X-ENDLIST

该选项通常和服务器配合使用。你设置了一个URL,然后浏览器可以通过从服务器获取m3u8​文件和视频切片文件。

远程播放HLS视频

在前面,我们已经知道了,HLS封装格式会生成以下文件:

output
├── index.m3u8
├── index0.ts
├── index1.ts
├── index2.ts
└── index3.ts

播放器拿到m3u8文件,它应该能够按照HLS协议并参考m3u8上的内容自主获取视频切片并播放。

这里我使用Qt6.6的QMediaPlayer​来播放m3u8文件(这样方便理解,实际上M3U8不包含视频数据,它只含有该怎样播放的信息)。

如何播放呢?抓住一个关键点:只要QMediaPlayer​拿到了M3U8文件,它就能够依照该M3U8文件播放上面的资源,而我们是不需要了解它具体如何播放的,因为播放器的开发者为我们完成了这部分工作。我们唯一需要关注的就是:如何让QMediaPlayer​获取到这份M3U8文件。

这里我在网上随便找一个M3U8文件的链接(随便找个不是很正规的视频网站一般都能够找到,查看其HTML代码你就能够发现隐藏在其中的M3U8文件):

具体是什么URL我就不放出来了。

我们可以发现该播放器需要一个M3U8的URL才能够播放。

我们将url=​后面的链接命名为VideoURL​,方便后续说明。

这里我们在浏览器中请求VideoURL​,看看会发生什么?结果就是,浏览器给我们下载了一个M3U8文件。

我们使用以下Qt6.6中的代码来播放VideoURL​,看看能否播放成功:

int main(int argc, char *argv[])
{QApplication a(argc, argv);QMediaPlayer player;player.setSource(QUrl("https://play.modujx11.com/20250104/pZZhNChc/index.m3u8"));QVideoWidget video_widget;player.setVideoOutput(&video_widget);video_widget.show();QAudioOutput audio_output;player.setAudioOutput(&audio_output);player.play();return a.exec();
}

结果是,播放成功了。

这说明了什么呢?说明了只要服务器能够提供一个接口,让浏览器能够通过访问该接口URL下载到M3U8文件,且M3U8中的资源是可以被浏览器访问到的,那么实现了HLS协议的播放器应该就能播放该M3U8文件。

利用上面实验出来的特性搭建视频平台的一些猜想

那么,如果我们的应用程序想要通过HLS协议实现视频远程播放的功能,首先客户端需要有M3U8文件并进行播放的能力;而服务器只需要提供下载M3U8文件和下载M3U8文件中对应的切片文件的接口即可。

若应用程序能够播放的视频是服务器端规定好的,用户无法上传任何视频,那么我们在服务器端完全可以自己在相应的文件夹下使用FFmpeg命令来慢慢进行切片。

若用户可在应用程序中上传视频,那么上传完成之后,服务器端如果不追求性能和定制化,我个人认为直接调用FFmpeg的命令行程序来完成HLS的切片是没有问题的。如果用户上传的视频的编码或者封装格式不合适,那么要么禁止用户上传这类视频;要么就在后端慢慢进行转码,若同一时间有大量转码的视频,那么对于性能的消耗将是灾难性的,因此大部分应用程序都不会允许用户将格式不合适的视频直接上传到服务器端,而是让用户自行找方法转码,转码完成后再发送到服务器端。

用户若是能够上传视频,如果应用程序具有一定的用户基数,那么上传的视频数量大概率是会逐渐增加的,对服务器的性能要求也会逐渐提高(不管是空间上还是时间上)。

应用程序可能一开始就是将这些视频开放给所有用户的,用户可以通过客户端来访问或自己上传的、或别人上传的视频数据。我们可能需要使用数据库存业务数据 + 文件系统存视频数据 + 数据库和文件系统之间存在某种数据上的联系,以此数据存储为基础构建服务器,然后客户端/前端再基于服务器的接口构建符合需求的交互界面。

实现一个简单的服务器来验证猜想

首先,在SpringBoot中实现这样一个Controller,用来下载服务器上的文件:

@Controller
public class FileDownloadController {private static final String FILE_DIR = "/videos";@GetMapping("/download/{dirname}/{filename:.+}")@ResponseBodypublic ResponseEntity<Resource> downloadFile(@PathVariable("dirname") String dirname,@PathVariable("filename") String filename) {String path = System.getProperty("user.dir") + FILE_DIR + "/" + dirname;Path media_list_path = Paths.get(path).resolve(filename).normalize();try {UrlResource resource =  new UrlResource(media_list_path.toUri());if (resource.exists() || resource.isReadable()) {return ResponseEntity.ok().contentType(MediaType.APPLICATION_OCTET_STREAM).header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\"").body(resource);}else {return ResponseEntity.notFound().build();}}catch (Exception e) {e.printStackTrace();return ResponseEntity.status(500).build();}}
}

接着,我们在指定的/videos​目录下创建好HLS封装的文件:

> ffmpeg -i hls_video.mp4 -f hls \
> -hls_base_url \  
> http://127.0.0.1:8080/download/hlstest1/ \
> -c copy \ 
> -hls_list_size 1000 \
> hlstest1/index.m3u8> tree .
.
├── hls_video.mp4
└── hlstest1├── index.m3u8├── index0.ts├── index1.ts...

大致内容如上所示,然后在替换Qt原本代码中的URL成http://localhost:8080/download/hlstest1/index.m3u8​,播放成功。

我这里的服务器运行在本机中,所以ip为localhost​。

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

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

相关文章

Apache和PHP:构建动态网站的黄金组合

在当今的互联网世界&#xff0c;网站已经成为了企业、个人和机构展示自己、与用户互动的重要平台。而在这些动态网站的背后&#xff0c;Apache和PHP无疑是最受开发者青睐的技术组合之一。这一组合提供了高效、灵活且可扩展的解决方案&#xff0c;帮助您快速搭建出强大的网站&am…

git相关操作笔记

git相关操作笔记 1. git init git init 是一个 Git 命令&#xff0c;用于初始化一个新的 Git 仓库。执行该命令后&#xff0c;Git 会在当前目录创建一个 .git 子目录&#xff0c;这是 Git 用来存储所有版本控制信息的地方。 使用方法如下&#xff1a; &#xff08;1&#xff…

Docker Desktop 构建java8基础镜像jdk安装配置失效解决

Docker Desktop 构建java8基础镜像jdk安装配置失效解决 文章目录 1.问题2.解决方法3.总结 1.问题 之前的好几篇文章中分享了在Linux(centOs上)和windows10上使用docker和docker Desktop环境构建java8的最小jre基础镜像&#xff0c;前几天我使用Docker Desktop环境重新构建了一个…

VUE + pdfh5 实现pdf 预览,主要用来uniappH5实现嵌套预览PDF

1. 安装依赖 npm install pdfh5 2. pdfh5 预览(移动端&#xff0c;h5) npm install pdfh5 , &#xff08;会报错&#xff0c;需要其他依赖&#xff0c;不能直接用提示的语句直接npm下载&#xff0c;依旧会报错&#xff0c;npm报错&#xff1a;These dependencies were not fou…

Node.js——fs(文件系统)模块

个人简介 &#x1f440;个人主页&#xff1a; 前端杂货铺 &#x1f64b;‍♂️学习方向&#xff1a; 主攻前端方向&#xff0c;正逐渐往全干发展 &#x1f4c3;个人状态&#xff1a; 研发工程师&#xff0c;现效力于中国工业软件事业 &#x1f680;人生格言&#xff1a; 积跬步…

Microsoft Azure Cosmos DB:全球分布式、多模型数据库服务

目录 前言1. Azure Cosmos DB 简介1.1 什么是 Azure Cosmos DB&#xff1f;1.2 核心技术特点 2. 数据模型与 API 支持2.1 文档存储&#xff08;Document Store&#xff09;2.2 图数据库&#xff08;Graph DBMS&#xff09;2.3 键值存储&#xff08;Key-Value Store&#xff09;…

springboot项目读取resources目录下文件

要用以下这种方式读取 classPathResource new ClassPathResource("template/test.docx");不能用以下这种获取绝对路径的方式&#xff0c;idea调试正常&#xff0c;但是部署window和linux的目录结构不一样&#xff0c;部署后会找不到文件&#xff0c;另外window直接…

Ruby语言的软件开发工具

Ruby语言的软件开发工具概述 引言 Ruby是一种简单且功能强大的编程语言&#xff0c;它以优雅的语法和灵活性而闻名。自1995年首次发布以来&#xff0c;Ruby已经被广泛应用于各种开发领域&#xff0c;特别是Web开发。随着Ruby语言的普及&#xff0c;相关的开发工具也日益丰富。…

C++例程:使用I/O模拟IIC接口(6)

完整的STM32F405代码工程I2C驱动源代码跟踪 一&#xff09;myiic.c #include "myiic.h" #include "delay.h" #include "stm32f4xx_rcc.h" //初始化IIC void IIC_Init(void) { GPIO_InitTypeDef GPIO_InitStructure;RCC_AHB1PeriphCl…

CNN-BiLSTM-Attention模型详解及应用分析

CNN-BiLSTM-Attention结构 CNN-BiLSTM-Attention结构是一种强大的深度学习架构,巧妙地结合了三种不同的技术优势:卷积神经网络(CNN)、双向长短期记忆网络(BiLSTM)和注意力机制(Attention)。这种创新性的组合使得模型能够在处理复杂序列数据时表现出色,尤其适用于自然…

2025年华为OD上机考试真题(Java)——整数对最小和

题目&#xff1a; 给定两个整数数组array1、array2&#xff0c;数组元素按升序排列。假设从array1、array2中分别取出一个元素可构成一对元素&#xff0c;现在需要取出k对元素&#xff0c;并对取出的所有元素求和&#xff0c;计算和的最小值。 注意&#xff1a;两对元素如果对应…

【Java知识】Groovy 一个兼容java的编程语言

groovy语言介绍 概述一、基本特点二、主要特性三、应用领域四、与Java的比较 基本语法特性一、基本语法二、数据类型三、运算符四、字符串五、方法六、闭包七、类与对象八、异常处理九、其他特性 集成到springboot项目1. 创建Spring Boot项目2. 添加Groovy依赖3. 编写Groovy类4…

Python网络爬虫:从入门到实战

Python以其简洁易用和强大的库支持成为网络爬虫开发的首选语言。本文将系统介绍Python网络爬虫的开发方法&#xff0c;包括基础知识、常用工具以及实战案例&#xff0c;帮助读者从入门到精通。 什么是网络爬虫&#xff1f; 网络爬虫&#xff08;Web Crawler&#xff09;是一种…

【vLLM 学习】安装

vLLM 是一款专为大语言模型推理加速而设计的框架&#xff0c;实现了 KV 缓存内存几乎零浪费&#xff0c;解决了内存管理瓶颈问题。 更多 vLLM 中文文档及教程可访问 →https://vllm.hyper.ai/ vLLM 是一个 Python 库&#xff0c;包含预编译的 C 和 CUDA (12.1) 二进制文件。 …

npm : 无法加载文件 D:\SoftFile\npm.ps1,因为在此系统上禁止运行脚本。

这个错误是由于 Windows PowerShell 的执行策略禁止执行脚本&#xff0c;导致无法运行 npm 命令。你可以通过以下步骤来解决这个问题&#xff1a; 以管理员身份运行 PowerShell&#xff1a; 点击“开始”菜单&#xff0c;搜索“PowerShell”&#xff0c;然后右键点击“Windows …

7 分布式定时任务调度框架

先简单介绍下分布式定时任务调度框架的使用场景和功能和架构&#xff0c;然后再介绍世面上常见的产品 我们在大型的复杂的系统下&#xff0c;会有大量的跑批&#xff0c;定时任务的功能&#xff0c;如果在独立的子项目中单独去处理这些任务&#xff0c;随着业务的复杂度的提高…

网络安全 | 网络安全法规:GDPR、CCPA与中国网络安全法

网络安全 | 网络安全法规&#xff1a;GDPR、CCPA与中国网络安全法 一、前言二、欧盟《通用数据保护条例》&#xff08;GDPR&#xff09;2.1 背景2.2 主要内容2.3 特点2.4 实施效果与影响 三、美国《加利福尼亚州消费者隐私法案》&#xff08;CCPA&#xff09;3.1 背景3.2 主要内…

Elixir语言的计算机基础

Elixir语言的计算机基础 引言 在当今这个快速发展的技术时代&#xff0c;编程语言层出不穷。Elixir作为一种较新的编程语言&#xff0c;以其高并发、低延迟和强大的容错能力受到越来越多开发者的青睐。它基于Erlang虚拟机&#xff08;BEAM&#xff09;&#xff0c;自然继承了…

mysql的mvcc理解

人阅读 一、说到mvcc就少不了事务隔离级别&#xff08;大白话解释&#xff09; 序列化&#xff08;SERIALIZABLE&#xff09;&#xff1a;事务之间完全隔离&#xff0c;当成一个序列&#xff0c;一个一个执行。 1 可重复读&#xff08;REPEATABLE READ&#xff09;&#xff…

“AI智能陪练培训服务系统,让学习更轻松、更高效

大家好&#xff0c;我是资深产品经理小李&#xff0c;今天咱们来侃侃一个新兴的教育辅助工具——AI智能陪练培训服务系统。这个系统可谓是教育培训行业的一股新势力&#xff0c;它究竟有什么神奇之处呢&#xff1f;下面我就跟大家伙儿好好聊聊。 一、什么是AI智能陪练培训服务系…