QT 实现解密m3u8文件
文章目录
- 概要
- 如何解密M3U8文件呢
- 实现思路和代码
- 序列图
- 网络请求
- 解密
- 结论
概要
视频文件很多已M3U8文件格式来提供,先复习下什么是M3U8文件!用QT的 mutimedia框架来播放视频时,有的视频加载慢,有的视频加载快,为啥?结论再最后
m3u8 文件实质是一个播放列表(playlist),其可能是一个媒体播放列表(Media Playlist),或者是一个主列表(Master Playlist)。但无论是哪种播放列表,其内部文字使用的都是 utf-8 编码。
当 m3u8 文件作为媒体播放列表(Meida Playlist)时,其内部信息记录的是一系列媒体片段资源,顺序播放该片段资源,即可完整展示多媒体资源。其格式如下所示:
#EXTM3U
#EXT-X-TARGETDURATION:10#EXTINF:9.009,
http://media.example.com/first.ts
#EXTINF:9.009,
http://media.example.com/second.ts
#EXTINF:3.003,
http://media.example.com/third.ts
对于点播来说,客户端只需按顺序下载上述片段资源,依次进行播放即可。而对于直播来说,客户端需要 定时重新请求 该 m3u8 文件,看下是否有新的片段数据需要进行下载并播放。
当 m3u8 作为主播放列表(Master Playlist)时,其内部提供的是同一份媒体资源的多份流列表资源(Variant Stream)。其格式如下所示:
#EXTM3U
#EXT-X-STREAM-INF:BANDWIDTH=150000,RESOLUTION=416x234,CODECS="avc1.42e00a,mp4a.40.2"
http://example.com/low/index.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=240000,RESOLUTION=416x234,CODECS="avc1.42e00a,mp4a.40.2"
http://example.com/lo_mid/index.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=440000,RESOLUTION=416x234,CODECS="avc1.42e00a,mp4a.40.2"
http://example.com/hi_mid/index.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=640000,RESOLUTION=640x360,CODECS="avc1.42e00a,mp4a.40.2"
http://example.com/high/index.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=64000,CODECS="mp4a.40.5"
http://example.com/audio/index.m3u8
该备用流资源指定了多种不同码率,不同格式的媒体播放列表,并且,该备用流资源也可同时提供不同版本的资源内容,比如不同语言的音频文件,不同角度拍摄的视屏文件等等。客户可以根据不同的网络状态选取合适码流的资源,并且最好根据用户喜好选择合适的资源内容。
以上,就是 m3u8 文件的大概内容
如何解密M3U8文件呢
示例:
M3U8文件是一种播放列表文件,用于存储和组织HLS(HTTP Live Streaming)流媒体数据。在M3U8文件中,EXT-X-KEY、URI和IV等字段是用于描述流媒体的关键信息。
EXT-X-KEY: 这个字段用于指定加密密钥的信息。它通常包含一个URI,该URI指向包含密钥的媒体文件。该字段还可能包含其他参数,如密钥的加密算法和密码等。
URI: 这个字段指定了媒体文件的URL地址。它用于告诉播放器从哪个位置获取媒体数据。
IV: 这个字段是初始化向量(Initialization Vector)的缩写,用于加密算法的初始化过程。在HLS流媒体中,每个媒体片段都使用不同的初始化向量进行加密,以确保每个片段的加密是独立的。
这些字段通常以特定的格式出现在M3U8文件中。下面是一个示例:
#EXTM3U
#EXT-X-STREAM-INF:PROGRAM-ID=1, BANDWIDTH=1280000
http://example.com/stream.m3u8?token=1234567890
#EXT-X-KEY:METHOD=AES-128, URI="http://example.com/key.txt", IV=0x00000000000000000000000000000001
#EXT-X-KEY:METHOD=AES-128, URI="http://example.com/key2.txt", IV=0x00000001000000010000000100000002
#EXT-X-I-FRAME-STREAM-INF:PROGRAM-ID=1, BANDWIDTH=512000, CODECS="mp4a.40.2", RESOLUTION=480x360, FRAME-RATE=15
#EXTINF:16.733333,
http://example.com/iframe.m3u8?token=abcdefghijklmnopqrstuvwxyz
在上述示例中,#EXTM3U标识了文件为M3U8播放列表的开始。#EXT-X-STREAM-INF指定了流媒体的信息,如节目ID和带宽。http://example.com/stream.m3u8?token=1234567890是媒体文件的URI。接下来的#EXT-X-KEY字段指定了加密密钥的信息,包括加密方法和密钥的URI以及初始化向量(IV)。在这个例子中,有两个密钥,每个密钥对应一个媒体片段。最后,#EXT-X-I-FRAME-STREAM-INF指定了I帧媒体流的信息,包括节目ID、带宽、编解码器、分辨率和帧率等。http://example.com/iframe.m3u8?token=abcdefghijklmnopqrstuvwxyz是I帧媒体文件的URI。
实现思路和代码
序列图

网络请求
QNetworkRequest request(url); //请求m3u8地址request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");addAllCookie(request);QNetworkReply *pNetworkResponse = pManager->get(request);m_tsContent.clear();m_keyContent.clear();m_ivData.clear();QObject::connect(pNetworkResponse, &QNetworkReply::finished, [=]{if (pNetworkResponse->error() == QNetworkReply::NoError){QByteArray bytes = pNetworkResponse->readAll();QJsonObject json_object = QJsonDocument::fromJson(bytes).object();if(json_object["code"].toInt() == 10000){if(!json_object["data"].isUndefined()){QJsonValue data = json_object["data"];QJsonValue urls = data["videoUrl"];// Test url hereQUrl videoUrl(urls["normal"].toString());QNetworkRequest videoRequest(videoUrl);videoRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");m_elaptimer.restart();QNetworkReply *pVideoNetworkResponse = pManager->get(videoRequest);connect(pVideoNetworkResponse, &QNetworkReply::finished, [=]{m3u8time = m_elaptimer.elapsed();if(pVideoNetworkResponse->error() == QNetworkReply::NoError) {QByteArray data = pVideoNetworkResponse->readAll();QString m3u8Content = QString::fromUtf8(data);QStringList lines = m3u8Content.split('\n');QString keyLine;QString tsLine;foreach (const QString &line, lines) {if (line.startsWith("#EXT-X-KEY:")) {keyLine = line;qDebug() << "[www]: keyline - " << keyLine;} else if (line.startsWith("https:")) {tsLine = line;qDebug() << "[www]: tsLine - " << tsLine;break;qDebug() << "[www]: test fist snippet and stop";}}QRegularExpression keyRegex("#EXT-X-KEY:METHOD=([A-Za-z0-9-]+),URI=\"([^\"]+)\",IV=([A-Za-z0-9-]+)");QRegularExpressionMatchIterator matchIterator = keyRegex.globalMatch(keyLine);QString method;QString keyUri;QString IVString;if (matchIterator.hasNext()) {QRegularExpressionMatch match = matchIterator.next();method = match.captured(1);keyUri = match.captured(2);IVString = match.captured(3);}qDebug() << "[www] method: " << method;qDebug() << "[www] keyUri: " << keyUri;qDebug() << "[www] tsLine: " << tsLine;qDebug() << "[www] IVString: " << IVString;QByteArray tsData = QByteArray();QByteArray keyData = QByteArray();QByteArray ivData = QByteArray::fromHex(IVString.right(IVString.size() - 2).toLatin1());m_ivData = ivData;qDebug() << "[www] ivData: " << ivData;QString keyContent;QString tsContent;//Get key from uriQUrl keyUrl(keyUri);QNetworkRequest keyQuest(keyUrl);keyQuest.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");beginKey = m_elaptimer.elapsed();QNetworkReply *keyResponse = pManager->get(keyQuest);connect(keyResponse, SIGNAL(error(QNetworkReply::NetworkError)),this, SLOT(httpError(QNetworkReply::NetworkError)));connect(keyResponse, &QNetworkReply::finished, [=]{if(keyResponse->error() == QNetworkReply::NoError) {QByteArray keyData = keyResponse->readAll();QString keyContent = QString::fromUtf8(keyData);qDebug() << "[www] keyContent:" << keyContent;qDebug() << "[www] keyData:" << keyData.toHex();m_keyContent = keyData;checkAesPara();} else {qDebug() << "[xiaole]" << keyResponse->errorString() ;}});//Get ts data from uriQUrl tsUrl(tsLine);QNetworkRequest tsRequest(tsUrl);tsRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");QNetworkReply *tsResponse = pManager->get(tsRequest);connect(tsResponse, SIGNAL(error(QNetworkReply::NetworkError)),this, SLOT(httpError(QNetworkReply::NetworkError)));connect(tsResponse, &QNetworkReply::finished, [=]{if(tsResponse->error() == QNetworkReply::NoError) {QByteArray tsData = tsResponse->readAll();QString tsContent = QString::fromUtf8(tsData);m_tsContent = tsData;checkAesPara();} });} else {}});}}}pNetworkResponse->close();pNetworkResponse->deleteLater();});
解密
#include "qaesencryption.h"extern "C" {#include <openssl/aes.h>#include <openssl/rand.h>QByteArray aesDecrypt(const QByteArray& cipherText, const QByteArray& key, const QByteArray& iv) {// 创建一个AES解密上下文AES_KEY decryptKey;AES_set_decrypt_key(reinterpret_cast<const unsigned char*>(key.constData()), 128, &decryptKey);// 解密数据QByteArray decryptedText(cipherText.size(), Qt::Uninitialized);AES_cbc_encrypt(reinterpret_cast<const unsigned char*>(cipherText.constData()),reinterpret_cast<unsigned char*>(decryptedText.data()),cipherText.size(),&decryptKey,const_cast<unsigned char*>(reinterpret_cast<const unsigned char*>(iv.data())),AES_DECRYPT);return decryptedText;}
}QByteArray qaesDecrypt(const QByteArray& cipherText, const QByteArray& key, const QByteArray& iv) {return QAESEncryption::Decrypt(QAESEncryption::AES_128, QAESEncryption::CBC, cipherText, key,iv, QAESEncryption::ZERO);
}
checkAesPara()
{if(!m_tsContent.isEmpty() && !m_keyContent.isEmpty()){qDebug()<<"keyContent"<< m_keyContent <<"tsContent"<< m_tsContent.length();QByteArray decTsData = aesDecrypt(m_tsContent, m_keyContent, m_ivData);QFile file("/home/test.ts"); //保存到本地文件if (file.open(QIODevice::ReadWrite | QIODevice::Truncate)){file.write(decTsData);file.close();}writeOver = m_elaptimer.elapsed();writeTime = writeOver-jiemiOver;}
}
结论
`
● ts片段越大,缓冲时间越长,反之亦然
● qt播放器并不需要一个完整的ts下载完才开始播放。
所以视频加载快慢,最大的因素是网速,还有就是跟TS片段大小有关系。
相关文章:
QT 实现解密m3u8文件
文章目录 概要如何解密M3U8文件呢实现思路和代码序列图网络请求解密 结论 概要 视频文件很多已M3U8文件格式来提供,先复习下什么是M3U8文件!用QT的 mutimedia框架来播放视频时,有的视频加载慢,有的视频加载快,为啥&am…...
论文阅读—— BiFormer(cvpr2023)
论文:https://arxiv.org/abs/2303.08810 github:GitHub - rayleizhu/BiFormer: [CVPR 2023] Official code release of our paper "BiFormer: Vision Transformer with Bi-Level Routing Attention" 一、介绍 1、要解决的问题:t…...
理解 fopen的 rwa r+w+a+ 参数含义
tags: C categories: C 理解 一图胜千言 我愿称之为最强 c - Difference between r and w in fopen() - Stack Overflow; 需要注意里面的a和 a, 区别在于 a 不可以读而 a可以读. c - Difference between r and w in fopen() - Stack Overflow; ModeReadWriteCreate New Fil…...
【强化学习】17 ——DDPG(Deep Deterministic Policy Gradient)
文章目录 前言DDPG特点 随机策略与确定性策略DDPG:深度确定性策略梯度伪代码代码实践 前言 之前的章节介绍了基于策略梯度的算法 REINFORCE、Actor-Critic 以及两个改进算法——TRPO 和 PPO。这类算法有一个共同的特点:它们都是在线策略算法,…...
驱动开发11-2 编写SPI驱动程序-点亮数码管
驱动程序 #include <linux/init.h> #include <linux/module.h> #include <linux/spi/spi.h>int m74hc595_probe(struct spi_device *spi) {printk("%s:%d\n",__FILE__,__LINE__);char buf[]{0XF,0X6D};spi_write(spi,buf,sizeof(buf));return 0; …...
Java使用pdfbox进行pdf和图片之间的转换
简介 pdfbox是Apache开源的一个项目,支持pdf文档操作功能。 官网地址: Apache PDFBox | A Java PDF Library 支持的功能如下图.引入依赖 <dependency><groupId>org.apache.pdfbox</groupId><artifactId>pdfbox-app</artifactId><version>…...
机器学习中的关键组件
机器学习中的关键组件 数据 每个数据集由一个个样本组成,大多时候,它们遵循独立同分布。样本有时也叫作数据点或数据实例,通常每个样本由一组称为特征或协变量的属性组成。机器学习会根据这些属性进行预测,预测得到的称为标签或…...
【JVM】JDBC案例打破双亲委派机制
🐌个人主页: 🐌 叶落闲庭 💨我的专栏:💨 c语言 数据结构 javaEE 操作系统 Redis 石可破也,而不可夺坚;丹可磨也,而不可夺赤。 JVM 打破双亲委派机制(JDBC案例…...
每天五分钟计算机视觉:池化层的反向传播
本文重点 卷积神经网络(Convolutional Neural Network,CNN)作为一种强大的深度学习模型,在计算机视觉任务中取得了巨大成功。其中,池化层(Pooling Layer)在卷积层之后起到了信息压缩和特征提取的作用。然而,池化层的反向传播一直以来都是一个相对复杂和深奥的问题。本…...
Docker的安装、基础命令与项目部署
文章目录 前言一、docker安装与MySQL部署1.Linux环境下docker的安装(1)基于CentOS7(2)基于Ubuntu 二、docker基础1.常见命令(1)快速创建一个mysql容器(MySQL得一键安装)。࿰…...
Nodejs和npm的使用方法和教程
Nodejs简介 Node.js 是一个开源和跨平台的 JavaScript 运行时环境。 它几乎是任何类型项目的流行工具! ( 运行环境,是不是很熟悉,对。就是 java JRE,Java 运行时环境) Node.js 在浏览器之外运行 V8 Java…...
机器学习---支持向量机的初步理解
1. SVM的经典解释 改编自支持向量机解释得很好 |字节大小生物学 (bytesizebio.net) 话说,在遥远的从前,有一只贪玩爱搞破坏的妖怪阿布劫持了善良美丽的女主小美,智勇双全 的男主大壮挺身而出,大壮跟随阿布来到了妖怪的住处&…...
【unity实战】Unity实现2D人物双击疾跑
最终效果 前言 我们要实现的功能是双击疾跑,当玩家快速地按下同一个移动键两次时能进入跑步状态 我假设快速按下的定义为0.2秒内,按下同一按键两次 简单的分析一下需求,实现它的关键在于获得按键按下的时间,我们需要知道第一次…...
Spring面试题:(二)基于xml方式的Spring配置
xml配置Bean的常见属性 id属性 name属性 scope属性 lazy-init属性 init-method属性和destroy属性 initializingBean方法 Bean实例化方式 ApplicationContext底层调用BeanFactory创建Bean,BeanFactory可以利用反射机制调用构造方法实例化Bean,也可采用工…...
XR Interaction ToolKit
一、简介 XR Interaction Toolkit是unity官方的XR交互工具包。 官方XRI示例地址:https://github.com/Unity-Technologies/XR-Interaction-Toolkit-Examples 2023.3.14官方博客,XRIT v2.3 https://blog.unity.com/engine-platform/whats-new-in-xr-int…...
spring-boot中实现分片上传文件
一、上传文件基本实现 1、前端效果图展示,这里使用element-ui plus来展示样式效果 2、基础代码如下 <template><div><el-uploadref"uploadRef"class"upload-demo":limit"1":on-change"handleExceed":auto-…...
【ICN综述】信息中心网络隐私安全
ICN基本原理: 信息中心网络也是需要实现在不可信环境下可靠的信息交换和身份认证 信息中心网络采用以数据内容为中心的传输方式代替现有IP 网络中以主机为中心的通信方式,淡化信息数据物理或逻辑位置的重要性,以内容标识为代表实现数据的查找…...
基于STC12C5A60S2系列1T 8051单片机EEPROM应用
基于STC12C5A60S2系列1T 8051单片机EEPROM应用 STC12C5A60S2系列1T 8051单片机管脚图STC12C5A60S2系列1T 8051单片机I/O口各种不同工作模式及配置STC12C5A60S2系列1T 8051单片机I/O口各种不同工作模式介绍STC12C5A60S2系列1T 8051单片机EEPROM介绍基于STC12C5A60S2系列1T 8051单…...
手撕排序之直接选择排序
前言: 直接选择排序是排序中比较简单的排序,同时也是时间复杂度不是很优的排序。 思想: 本文主要讲解直接选择排序的优化版本。 我们经过一次遍历直接将该数列中最大的和最小的值挑选出来,如果是升序,就将最小的和…...
洛谷 P1359 租用游艇
题目链接 P1359 租用游艇 普及 题目描述 长江游艇俱乐部在长江上设置了 n n n 个游艇出租站 1 , 2 , 3 , . . . , n 1,2,3,...,n 1,2,3,...,n,游客可在这些游艇出租站租用游艇,并在下游的任何一个游艇出租站归还游艇。游艇出租站 i i i 到游艇出租站…...
基于算法竞赛的c++编程(28)结构体的进阶应用
结构体的嵌套与复杂数据组织 在C中,结构体可以嵌套使用,形成更复杂的数据结构。例如,可以通过嵌套结构体描述多层级数据关系: struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...
css实现圆环展示百分比,根据值动态展示所占比例
代码如下 <view class""><view class"circle-chart"><view v-if"!!num" class"pie-item" :style"{background: conic-gradient(var(--one-color) 0%,#E9E6F1 ${num}%),}"></view><view v-else …...
Zustand 状态管理库:极简而强大的解决方案
Zustand 是一个轻量级、快速和可扩展的状态管理库,特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...
centos 7 部署awstats 网站访问检测
一、基础环境准备(两种安装方式都要做) bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats࿰…...
【磁盘】每天掌握一个Linux命令 - iostat
目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat(I/O Statistics)是Linux系统下用于监视系统输入输出设备和CPU使…...
大学生职业发展与就业创业指导教学评价
这里是引用 作为软工2203/2204班的学生,我们非常感谢您在《大学生职业发展与就业创业指导》课程中的悉心教导。这门课程对我们即将面临实习和就业的工科学生来说至关重要,而您认真负责的教学态度,让课程的每一部分都充满了实用价值。 尤其让我…...
selenium学习实战【Python爬虫】
selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...
代理篇12|深入理解 Vite中的Proxy接口代理配置
在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...
OPENCV形态学基础之二腐蚀
一.腐蚀的原理 (图1) 数学表达式:dst(x,y) erode(src(x,y)) min(x,y)src(xx,yy) 腐蚀也是图像形态学的基本功能之一,腐蚀跟膨胀属于反向操作,膨胀是把图像图像变大,而腐蚀就是把图像变小。腐蚀后的图像变小变暗淡。 腐蚀…...
算法岗面试经验分享-大模型篇
文章目录 A 基础语言模型A.1 TransformerA.2 Bert B 大语言模型结构B.1 GPTB.2 LLamaB.3 ChatGLMB.4 Qwen C 大语言模型微调C.1 Fine-tuningC.2 Adapter-tuningC.3 Prefix-tuningC.4 P-tuningC.5 LoRA A 基础语言模型 A.1 Transformer (1)资源 论文&a…...
