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。
相关文章:
FFmpeg Muxer HLS
使用FFmpeg命令来研究它对HLS协议的支持程度是最好的方法: ffmpeg -h muxerhls Muxer HLS Muxer hls [Apple HTTP Live Streaming]:Common extensions: m3u8.Default video codec: h264.Default audio codec: aac.Default subtitle codec: webvtt. 这里面告诉我…...
如何用SQL语句来查询表或索引的行存/列存存储方式|OceanBase 用户问题集锦
一、问题背景 自OceanBase 4.3.0版本起,支持了列存引擎,允许表和索引以行存、纯列存或行列冗余的形式创建,且这些存储方式可以自由组合。除了使用 show create table命令来查看表和索引的存储类型外,也有用户询问如何通过SQL语句…...
回归预测 | MATLAB实GRU多输入单输出回归预测
回归预测 | MATLAB实GRU多输入单输出回归预测 目录 回归预测 | MATLAB实GRU多输入单输出回归预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 回归预测 | MATLAB实GRU多输入单输出回归预测。使用GRU作为RNN的一种变体来处理时间序列数据。GRU相比传统的RNN有较好的记…...
【OpenGL/Assimp】渲染模型、半透明材质与封装光源
文章目录 渲染成果Assimp库准备:Mesh类修改:透明贴图使用:光源封装:使用方式在如下测试环境中: 渲染成果 Assimp库准备: 从GitHub拉取源码,根据网络教程,借助CMake生成VS工程项目&a…...
pandas与sql对应关系【帮助sql使用者快速上手pandas】
本页旨在提供一些如何使用pandas执行各种SQL操作的示例,来帮助SQL使用者快速上手使用pandas。 目录 SQL语法一、选择SELECT1、选择2、添加计算列 二、连接JOIN ON1、内连接2、左外连接3、右外连接4、全外连接 三、过滤WHERE1、AND2、OR3、IS NULL4、IS NOT NULL5、B…...
Linux WEB漏洞
定义:Linux Web 漏洞是指在基于 Linux 操作系统的 Web 应用程序、Web 服务器软件或者相关的网络服务配置中存在的安全弱点。这些漏洞可能导致攻击者未经授权访问敏感信息、篡改网页内容、执行恶意代码,甚至完全控制服务器。 常见类型及原理 SQL 注入漏…...
音视频入门基础:RTP专题(2)——使用FFmpeg命令生成RTP流
通过FFmpeg命令可以将一个媒体文件转推RTP: ffmpeg -re -stream_loop -1 -i input.mp4 -c:v copy -an -f rtp rtp://192.168.0.102:5400 但是通过ffplay尝试播放上述产生的RTP流时会报错:“Unable to receive RTP payload type 96 without an SDP file …...
大语言模型预训练、微调、RLHF
转发,如有侵权,请联系删除: 1.【LLM】3:从零开始训练大语言模型(预训练、微调、RLHF) 2.老婆饼里没有老婆,RLHF里也没有真正的RL 3.【大模型微调】一文掌握7种大模型微调的方法 4.基于 Qwen2.…...
vue3后台系统动态路由实现
动态路由的流程:用户登录之后拿到用户信息和token,再去请求后端给的动态路由表,前端处理路由格式为vue路由格式。 1)拿到用户信息里面的角色之后再去请求路由表,返回的路由为tree格式 后端返回路由如下: …...
解决idea中无法拖动tab标签页的问题
1、按 Ctrl Alt S 打开设置,找到路径 File | Settings | Appearance & Behavior | Appearance 2、去掉勾选 Drag-and-drop with Alt pressed only 即可...
WMS仓库管理系统,Vue前端开发,Java后端技术源码(源码学习)
一、项目背景和建设目标 随着企业业务的不断扩展,仓库管理成为影响生产效率、成本控制及客户满意度的重要环节。为了提升仓库作业的透明度、准确性和效率,本方案旨在构建一套全面、高效、易用的仓库管理系统(WMS)。该系统将涵盖库…...
25/1/12 嵌入式笔记 学习esp32
了解了一下位选线和段选线的知识: 位选线: 作用:用于选择数码管的某一位,例如4位数码管的第1位,第2位) 通过控制位选线的电平(高低电平),决定当前哪一位数码管处于激活状…...
【NLP】ELMO、GPT、BERT、BART模型解读及对比分析
文章目录 一、基础知识1.1 Word Embedding(词嵌入)1.2 词嵌入模型1.3 神经网络语言模型NNLM 二、ELMO2.1 ELMO的提出2.2 ELMO核心思想2.3 ELMO的优缺点 三、GPT3.1 Transformer3.2 GPT简介3.3 GPT模型架构3.4 预训练及微调3.5 GPT和ELMO对比 四、BERT4.1…...
go语言学习(数组,切片,字符串)
字符串 如果里面存储的是汉字,那么其实就是存储的是UTF--8编码,所以一个字会对应多个字节.如果想要获取汉字的个数,可以使用rune,来处理unicode字符 length: utf8.RuneCountInString( s) 如果只使用len()获取的是字节的个数, 字符串的功能 1,获取字节长度 len(xx) 2,获取字…...
PM 实战 - 智能药盒PRD + 市场规模分析
写在前面 智能硬件 PRD 实例资源很少,Po下个人作品,假定前提为to Boss需求,目标在于覆盖产品设计核心部分(用户画像Persona、产品逻辑图、产品架构图、软件原型图、硬件低保真设计、用例Use Case、硬件标准)。不是申请…...
SQL刷题快速入门(二)
其他章节:SQL刷题快速入门(一) 承接上一章节,本章主要讲SQL的运算符、聚合函数、SQL保留小数的几种方式三个部分 运算符 SQL 支持多种运算符,用于执行各种操作,如算术运算、比较、赋值、逻辑运算等。以下…...
hive迁移后修复分区慢,怎么办?
我有1个30TB的分区表,客户给的带宽只有600MB,按照150%的耗时来算,大概要迁移17小时。 使用hive自带的修复分区命令(一般修复分区比迁移时间长一点),可能要花24小时。于是打算用前面黄大佬的牛B方案。 Hive增…...
代码随想录算法训练营day27
代码随想录算法训练营 —day27 文章目录 代码随想录算法训练营前言一、贪心算法理论基础二、455.分发饼干三、376. 摆动序列53. 最大子数组和总结 前言 今天是算法营的第27天,希望自己能够坚持下来! 今日任务: ● 贪心算法理论基础 ● 455.…...
python 代码使用 DeepXDE 库实现了一个求解二维非线性偏微分方程(PDE)的功能
import deepxde as dde import numpy as np import matplotlib.pyplot as plt import tensorflow as tf# 设置时空计算域 Lx 1 # x 范围从 0 到 1 Ly 1 # y 范围从 0 到 1 Lt 0.05 # t 范围从 0 到 0.05 geom dde.geometry.Rectangle([0, 0], [Lx, Ly]) # 空间域 timed…...
【Go】:深入解析 Go 1.24:新特性、改进与最佳实践
前言 Go 1.24 尚未发布。这些是正在进行中的发布说明。Go 1.24 预计将于 2025 年 2 月发布。本文将深入探讨 Go 1.24 中引入的各项更新,并通过具体示例展示这些变化如何影响日常开发工作,确保为读者提供详尽而有价值的参考。 新特性及改进综述 HTTP/2 …...
CTF show Web 红包题第六弹
提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框,很难让人不联想到SQL注入,但提示都说了不是SQL注入,所以就不往这方面想了 先查看一下网页源码,发现一段JavaScript代码,有一个关键类ctfs…...
python打卡day49
知识点回顾: 通道注意力模块复习空间注意力模块CBAM的定义 作业:尝试对今天的模型检查参数数目,并用tensorboard查看训练过程 import torch import torch.nn as nn# 定义通道注意力 class ChannelAttention(nn.Module):def __init__(self,…...
2.Vue编写一个app
1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...
【JavaSE】绘图与事件入门学习笔记
-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角,以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向,距离坐标原点x个像素;第二个是y坐标,表示当前位置为垂直方向,距离坐标原点y个像素。 坐标体系-像素 …...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
重启Eureka集群中的节点,对已经注册的服务有什么影响
先看答案,如果正确地操作,重启Eureka集群中的节点,对已经注册的服务影响非常小,甚至可以做到无感知。 但如果操作不当,可能会引发短暂的服务发现问题。 下面我们从Eureka的核心工作原理来详细分析这个问题。 Eureka的…...
回溯算法学习
一、电话号码的字母组合 import java.util.ArrayList; import java.util.List;import javax.management.loading.PrivateClassLoader;public class letterCombinations {private static final String[] KEYPAD {"", //0"", //1"abc", //2"…...
Go 并发编程基础:通道(Channel)的使用
在 Go 中,Channel 是 Goroutine 之间通信的核心机制。它提供了一个线程安全的通信方式,用于在多个 Goroutine 之间传递数据,从而实现高效的并发编程。 本章将介绍 Channel 的基本概念、用法、缓冲、关闭机制以及 select 的使用。 一、Channel…...
tomcat入门
1 tomcat 是什么 apache开发的web服务器可以为java web程序提供运行环境tomcat是一款高效,稳定,易于使用的web服务器tomcathttp服务器Servlet服务器 2 tomcat 目录介绍 -bin #存放tomcat的脚本 -conf #存放tomcat的配置文件 ---catalina.policy #to…...
Linux系统部署KES
1、安装准备 1.版本说明V008R006C009B0014 V008:是version产品的大版本。 R006:是release产品特性版本。 C009:是通用版 B0014:是build开发过程中的构建版本2.硬件要求 #安全版和企业版 内存:1GB 以上 硬盘…...
