ijkplayer 自定义协议播放加密内容 Android
想对播放的音视频进行加密,防止资源被盗用,该怎么办呢?
这篇文章从自定义协议的角度来提供一中实现思路。在 ijkplayer 的基础上,通过实现自定义协议对文件进行解密。边解边播,以此为基础,还可以实现在线资源边下载边解密边播放。
FFmpeg 文件协议
ffmpeg 中定义了 URLProtocol, 对接入 ffmpeg 中的各种协议进行了统一的抽象,这里可以理解为 C++ 中的抽象基类,并基于此抽象协议实现了 file、http、ftp、cache 等许多不同的具体文件传输协议。
而在 libavformat 模块中的 avio.c aviobuf.c 两个文件则是在 ffmpeg 对文件传输协议抽象的基础上进行的封装,使对文件的操作能够在 ffmpeg 中其他对文件协议细节不关心的地方使用。这就好比是面向对象中的模版模式。
ffmpeg 中的文件协议简单讲就是这样,更深入复杂的我也还没具体研究,本文中也用不到。
URLProtocol 的部分代码如下,这里我只留下了最常用的一部分。URLProtocol 中定义了很多函数指针,在 avio.c 的模板模式代码中用到。
代码解读
复制代码
typedef struct URLProtocol { const char *name; int (*url_open)( URLContext *h, const char *url, int flags); int (*url_read)( URLContext *h, unsigned char *buf, int size); int (*url_write)(URLContext *h, const unsigned char *buf, int size); int64_t (*url_seek)( URLContext *h, int64_t pos, int whence); int (*url_close)(URLContext *h); int priv_data_size; const AVClass *priv_data_class; } URLProtocol;
通过实现 URLProtocol 中的函数指针,就完成了一个可以用在 ffmpeg 中的文件协议。比如我找了一番 ffmpeg 中一个比较简单的文件协议,从实现上可以看出这个 bluray 在ffmpeg 中是一个只读协议。
代码解读
复制代码
const URLProtocol ff_bluray_protocol = { .name = "bluray", .url_close = bluray_close, .url_open = bluray_open, .url_read = bluray_read, .url_seek = bluray_seek, .priv_data_size = sizeof(BlurayContext), .priv_data_class = &bluray_context_class, };
ffmpeg 中通过一个列表保存了所有实现了的文件协议,进行各种文件操作的第一步,就是先根据 url(filename)找到对应的 URLProtocol。这段代码中通过匹配 url 中的 scheme 字符串,找到并返回对应的 protocol 指针。
代码解读
复制代码
static const struct URLProtocol *url_find_protocol(const char *filename) { // ...... const URLProtocol **protocols = ffurl_get_protocols(NULL, NULL); if (!protocols) return NULL; for (i = 0; protocols[i]; i++) { const URLProtocol *up = protocols[i]; if (!strcmp(proto_str, up->name)) { av_freep(&protocols); return up; } } // ...... }
如果对 ffmpeg 有一定的了解,可能会知道 ffmpeg 中的 Codec 还有 avcodec_register。FFmpeg 通过接口 avcodec_register 在运行时动态添加编解码器。但是却没有类似的 avformat_register 或者 av_urlprotocol_register 来实现运行时动态注册自定义的 protocol。为什么不实现这个的原因没有研究过,所以不能瞎吹。但是我们可以改 ffmpeg 源代码呀。
ijkplayer 的作者就改了 ffmpeg ,新增了几个 URLProtocol 并加入到了默认的 protocols 数组中,这些新增的 protocols 在 ffmpeg 源码中默认是空的实现。但是在 ffmpeg 中预留了接口可以在 ijkplayer 中使用后期实现的 protocol 替换这个空的 protocol。相当于添加了作用类似于 av_urlprotocol_register 但是有一定限制的接口。
ijkmediadatasource 协议实现
在 ijkplayer 项目的 ijkmediadatasource.c 源文件中,实现了一种 URLProtocol
代码解读
复制代码
URLProtocol ijkimp_ff_ijkmediadatasource_protocol = { .name = "ijkmediadatasource", .url_open2 = ijkmds_open, .url_read = ijkmds_read, .url_seek = ijkmds_seek, .url_close = ijkmds_close, .priv_data_size = sizeof(Context), .priv_data_class = &ijkmediadatasource_context_class, };
在每个具体的函数指针实现中,又通过 J4A 生成的胶水代码去调用到 java 层的代码。
进一步解释一下, setDataSource 的时候调用到这一块代码,callback 已经是一个具体的 java 对象了,并且是一个 tv/danmaku/ijk/media/player/misc/IMediaDataSource 接口的具体实现。
代码解读
复制代码
IjkMediaPlayer_setDataSourceCallback(JNIEnv *env, jobject thiz, jobject callback) { nativeMediaDataSource = jni_set_media_data_source(env, thiz, callback); snprintf(uri, sizeof(uri), "ijkmediadatasource:%"PRId64, nativeMediaDataSource); retval = ijkmp_set_data_source(mp, uri); }
jni_set_media_data_source 函数新建一个 NDK 环境对 callback 这个 jobject 的全局引用,并把引用作为 intptr_t 类型保存在 IjkMediaPlayer 的 mNativeMediaDataSource 字段中,方便后面再次使用以及最后需要close 是用到。 通过这样的转换之后,url 进入 ffmpeg avformat 逻辑中就成了 “ijkmediadatasource:2234234290" 这样的形式。 再通过 url_find_protocol 找到 ijkimp_ff_ijkmediadatasource_protocol ,所有的对这个文件协议的操作又回到 ijkplayer 的代码中了。
在 ijkmds_open 中,只需要把 intptr_t 的变量转换成 jobject 就行,不必实际去打开某个文件。 ijkmds_read 、 ijkmds_seek 函数通过 J4A 去调用 IMediaDataSource 接口的 readAt 方法。 readAt 方法多了 pos 参数,所以 read 和 seek 都可以通过 readAt 实现, ijkmds_close 调用 IMediaDataSource 接口的 close 方法,并释放 NDK 环境中的 jobject 全局引用。
通过这一层套一层的接口定义实现、struct 函数指针定义实现, java 代码中设置的 dataSource 在 ijkplayer 、ffmpeg 中打了个转之后最终又回到 java 代码中。
jni4android 自动生成代码
jni4android 是 B 站出品的开源项目 github.com/bilibili/jn…。能够根据简单的 java 接口描述代码生成 ndk 胶水代码,方便在 ndk c/c++ 环境中调用 java 代码。
ijkmediadatasource.c 源文件中 JNI 相关调用的代码,都是 j4a 自动生成的。按照 jni4android 中 readme 编译好 j4a 之后,结合 ijkplayer/ijkmedia/j4a 中的 makefile 文件,就可以快速掌握 j4a 在 ijkplayer 中的作用和使用方法了。
加密和解密
前面代码分析中解释了 ijkplayer 中自定义文件协议到底是怎么一回事,以及其中的函数调用过程怎么样的。
分析完了,终于可以开始代码敲起来。我们先对一个视频文件加密,然后实现解密的文件协议并在播放中使用。
这里采用古老的凯撒加解密算法,加密过程就是每个 byte 加个数字,解密过程就是每个 byte 减去个数字。相当简单,很容易暴力破解,这里仅作为示例参考。
加密过程如下(截取部分代码):
代码解读
复制代码
func main() { ibuf := bufio.NewReader(inputfile) obuf := bufio.NewWriter(outputfile) buf := make([]byte, 128) for { n, err := ibuf.Read(buf) if err == nil { for index := 0; index < n; index++ { buf[index] = buf[index] + byte(cck) } obuf.Write(buf[:n]) } else if err == io.EOF { break } } obuf.Flush() }
解密的 java IMediaDataSource 实现如下,这里只给出关键部分,其他的和项目中 FileMediaDataSource 一样。
代码解读
复制代码
public class CCFileMediaDataSource implements IMediaDataSource { @Override public int readAt(long position, byte[] buffer, int offset, int size) throws IOException { if (mFile.getFilePointer() != position) mFile.seek(position); if (size == 0) return 0; int s = mFile.read(buffer, 0, size); for (int i = 0; i < s; i++) { buffer[i] = (byte)(buffer[i] - 10); } return s; } }
什么情况下会用到 CCFileMediaDataSource 呢? 简单粗暴,把项目中 IjkVieoView.java 用到 FileMediaDataSource 的地方都改成 CCFileMediaDataSource,其实也没几处,然后别忘了在 demo 中设置选项里选中使用MediaDataSource。
IMediaDataSource 其他实现
RandomAccessFile 实现的 IMediaDataSource, 支持本地保存的文件。
代码解读
复制代码
public class FileMediaDataSource implements IMediaDataSource { private RandomAccessFile mFile; private long mFileSize; public FileMediaDataSource(File file) throws IOException { mFile = new RandomAccessFile(file, "r"); mFileSize = mFile.length(); } @Override public int readAt(long position, byte[] buffer, int offset, int size) throws IOException { if (mFile.getFilePointer() != position) mFile.seek(position); if (size == 0) return 0; return mFile.read(buffer, 0, size); } @Override public long getSize() throws IOException { return mFileSize; } @Override public void close() throws IOException { mFileSize = 0; mFile.close(); mFile = null; } }
InputStream 实现的 IMediaDataSource,可以用于播放 asset 资源文件
代码解读
复制代码
public class StreamDataSource implements IMediaDataSource { private InputStream mIs; private long mPosition = 0; public StreamDataSource(InputStream mIs) { this.mIs = mIs; } @Override public int readAt(long position, byte[] buffer, int offset, int size) throws IOException { if (size <= 0) return size; if (mPosition != position) { mIs.reset(); mPosition = mIs.skip(position); } int length = mIs.read(buffer, offset, size); mPosition += length; return length; } @Override public long getSize() throws IOException { return mIs.available(); } @Override public void close() throws IOException { if (mIs != null) mIs.close(); mIs = null; } }
代码解读
复制代码
AssetManager assetManager = mContext.getAssets(); InputStream is = assetManager.open("asset_file_path", AssetManager.ACCESS_RANDOM); mIjkMediaPlayer.setDataSource(new StreamDataSource(is));
相关文章:
ijkplayer 自定义协议播放加密内容 Android
想对播放的音视频进行加密,防止资源被盗用,该怎么办呢? 这篇文章从自定义协议的角度来提供一中实现思路。在 ijkplayer 的基础上,通过实现自定义协议对文件进行解密。边解边播,以此为基础,还可以实现在线资…...
【kill】Centos/Linux 如何杀死那该死的进程?
简介 系统版本:Centos7.6 kill 命令发送指定的信号到指定的进程或者进程组。如果该信号没有指定,则发送SIGTERM信号(15),即终止信号。如果SIGTERM信号(15)无法终止,可使用SIGKILL&am…...
Cadence23学习笔记(十七)
吴老师的网站里面有很多cadence的开源项目,可以用来学习: 全志 H3 SOC 官方参考设计 DD3 16bitX2 原理图及PCB – 吴川斌的博客 (mr-wu.cn) cadence设置显示实时的走线长度: 在allegro进行布线时可以实时显示当先布线的实际长度有助于pcb的…...
WinFrom调用webapi接口方法及其应用实例
1.WinFrom调用webapi接口方法 最近项目要在winfrom项目中调用webAPI,故在网上查找资料,找到了一个WinFrom调用webapi接口的通用方法,关键代码: #region WinFrom调用webapi接口通用方法private async Task<string> InvokeWe…...
智能巡检企业级域名 SSL 证书
一、概述 SSL 证书是一种数字证书,用于在用户和服务器之间建立加密链接,确保数据传输的安全性,防止数据在传输过程中被截获或篡改。SSL 证书不仅保护了数据传输过程中的隐私和完整性,还可以帮助验证网站的身份,防止钓…...
海思35XX系列(三)sensor(传感器)
刚开始接触这个概念的时候感觉比较模糊,简单记录一下吧 Sensor(传感器)是一种可以感知外部环境并将感知到的信息转化为可用的电信号或其他形式的工具。传感器广泛应用于电子设备、工业自动化、汽车、医疗器械等领域,用于测量、监…...
dfs(续做)
1.混境之地5 #include<bits/stdc.h> using namespace std; typedef long long ll; const int dx[4] {0,1,0,-1}; const int dy[4] {1,0,-1,0}; int n,m,k,a,b,c,d,sign0; int h[1010][1010],visit[1010][1010]; void dfs(int x,int y,bool used) {if(xc&&yd){si…...
OpenStack;异构算力网络架构;算力服务与交易技术;服务编排与调度技术
目录 OpenStack 一、OpenStack概述 二、OpenStack的主要组件及功能 三、OpenStack的架构 四、OpenStack的应用场景 异构算力网络架构 算力服务与交易技术 服务编排与调度技术 OpenStack 是一个开源的云计算管理平台项目,由NASA(美国国家航空航天局)和Rackspace合作…...
PLC-Recorder对于数据采集时间戳偏差的修正功能
目录 一、修正原理 二、使用步骤 1、初始状态,计算修正系数 2、启动和使用 3、修正系数的手动修改 三、修正前后的效果对比 1、修正前的时间偏差曲线 2、修正后的效果曲线 四、注意事项 1、控制器可能的时钟波动 2、平移参数评估 3、参数保存 五、关于…...
自定义监控
代码说明: 导入必要的库 import time import psutil import GPUtil from prometheus_client import start_http_server, Summary, Counter, Gaugepsutil:用于获取系统的CPU、内存、磁盘和网络信息。GPUtil:用于获取GPU信息。prometheus_cli…...
关于使用php的mpdf插件遇到的一些问题
一.插件版本 "mpdf/mpdf": "^8.0", 二.报错:Undefined index: list_style_type 这个是插件无法识别 li 标签导致,生成pdf是加入下面代码 <style> li { list-style-type: none; list-style-image: none; list-style-positi…...
电脑截图,颜色变淡的问题解决
解决 Windows 11 在 HDR 模式下截图 Edge 浏览器界面时画面过曝的问题-腾讯云开发者社区-腾讯云 (tencent.com) 主要是浏览器用错了东西,调一下就好,详情看链接 联想拯救者y9000p为例,选择sRGB,就好啦,拜拜喽&#x…...
uniApp跳转外链
创建一个新的页面,在该页面中使用web-view组件加载外部URL:pages/web-view/web-view <template><view><uni-list><uni-list-itemtitle"打开外部链接"click"openExternalLink"></uni-list-item></…...
科技云报道:大模型引领技术浪潮,AI安全治理面临“大考”
科技云报道原创。 从文生文到文生图,再到文生视频,近年来,以ChatGPT、Sora等为代表的大模型引领了全球人工智能技术与产业的新一轮浪潮。2024年更是被业内称为大模型应用爆发元年。 年初,Sora横空出世验证了Scalling Law在视频生…...
SpringSecurity+Mysql数据库实现用户安全登录认证
Spring Security 是一个提供身份认证、授权和防范常见攻击的安全权限框架。无论是对命令式,还是响应式web应用程序都完美支持,现在主要用作保护基于 Spring 框架的应用程序的事实标准。相对于shiro来说,SpringSecurity功能更加复杂而且更加强…...
虚拟网卡添加ip
1.虚拟机网卡添加 1.进入虚拟机设置添加网卡即网络适配器 2.配置文件修改 1.查看网卡是否添加成功 ip ad ifconfig 其中ens161就是我们新添加的设备同时这个ens161也是我们硬件名字 2.进入系统配置文件 cd /etc/sysconfig/network-scripts/#配置文件目录[rootlocalhost ne…...
Unity向量线性插值Lerp
inline glm::vec3 Lerp(glm::vec3 start, glm::vec3 end, float t){t glm::clamp(t, 0.0f, 1.0f);return (start*(1-t)end*t); }...
fatal: Could not read from remote repository. 解决方法
问题描述: Git : fatal: Could not read from remote repository. Please make sure you have the correct access rights and the repository exists。 解决方法: 当在网上尝试大量方法仍然失败的时候,不妨试试这个方法。 在 github 上&…...
postman查询单条数据Get方法,无任何输出,idea后端也没有任何数据和提示的解决方法
问题描述: 正常使用postman测试,输入内容没有错误,但是却没有任何消息 后端也是,没有任何消息: 解决方法: 问题的原因主要是因为postman: 我们只需要新建一个页面,把刚才的查询语…...
query怎么改写,才能实现高质量的知识问答系统
为了实现高质量的知识问答系统,query改写需要综合利用多种技术,确保改写后的查询更具语义性、准确性和完整性。以下是具体的步骤和方法: 1. 同义词和短语替换 步骤: 建立同义词库:使用现有的同义词词典或根据特定领…...
day52 ResNet18 CBAM
在深度学习的旅程中,我们不断探索如何提升模型的性能。今天,我将分享我在 ResNet18 模型中插入 CBAM(Convolutional Block Attention Module)模块,并采用分阶段微调策略的实践过程。通过这个过程,我不仅提升…...
.Net框架,除了EF还有很多很多......
文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...
8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...
相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...
拉力测试cuda pytorch 把 4070显卡拉满
import torch import timedef stress_test_gpu(matrix_size16384, duration300):"""对GPU进行压力测试,通过持续的矩阵乘法来最大化GPU利用率参数:matrix_size: 矩阵维度大小,增大可提高计算复杂度duration: 测试持续时间(秒&…...
c#开发AI模型对话
AI模型 前面已经介绍了一般AI模型本地部署,直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型,但是目前国内可能使用不多,至少实践例子很少看见。开发训练模型就不介绍了&am…...
今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存
文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...
有限自动机到正规文法转换器v1.0
1 项目简介 这是一个功能强大的有限自动机(Finite Automaton, FA)到正规文法(Regular Grammar)转换器,它配备了一个直观且完整的图形用户界面,使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...
Angular微前端架构:Module Federation + ngx-build-plus (Webpack)
以下是一个完整的 Angular 微前端示例,其中使用的是 Module Federation 和 npx-build-plus 实现了主应用(Shell)与子应用(Remote)的集成。 🛠️ 项目结构 angular-mf/ ├── shell-app/ # 主应用&…...
Python+ZeroMQ实战:智能车辆状态监控与模拟模式自动切换
目录 关键点 技术实现1 技术实现2 摘要: 本文将介绍如何利用Python和ZeroMQ消息队列构建一个智能车辆状态监控系统。系统能够根据时间策略自动切换驾驶模式(自动驾驶、人工驾驶、远程驾驶、主动安全),并通过实时消息推送更新车…...
