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. 同义词和短语替换 步骤: 建立同义词库:使用现有的同义词词典或根据特定领…...
Python实战——轻松实现动态网页爬虫(附详细源码)
大家好,我是东眠的鱼,专注原创,致力于用浅显易懂的语言分享爬虫、数据分析及可视化等干货,希望人人都能学到新知识。<文末附带精品籽料哦,也可以和博主一起学Python呀!> 项目背景 有同学自学爬虫时…...
Python应用—利用opencv实现图像匹配
1.创作需求 我们通常需要从一幅图中找到自己想要的信息,例如从一堆表情中找到,自己需要的表情。 2.创作思路 1.使用模板图片作为卷积核与原图进行卷积运算,得出匹配度超过某一阈值的范围。 2.对该部分进行框选 3.工具 主要使用opencv库实现,通过 cv2.matchTemplate(…...
Excel函数基础(二)
1、单元格名称 框选单元格(不含标题),公式选项卡->定义名称, 或者框选单元格后,在左上角的栏位直接输入要定义的名字,按下enter键即可 这样在使用函数时,数据范围参数可以用定义的名称来替…...
学习大数据DAY30 python基础语法3
目录 上机练习 5 File(文件) 读写 文本文件写 文本文件读 csv 文件写 csv 文件读 异常 自定义异常 上机练习 6 上机练习 5 上机练习5的作业我昨天就写完了,但是因为它是今天的作业,我就今天发咯。 # atm 机模拟器:使用 main 入口&…...
一文弄清Java的四大引用及其两大传递
开场白 Hello大家好呀,我是CodeCodeBond✊最近在复习很多很多的基础知识,有了很多新的感悟~ 话不多说,直接发车✈ 四大引用 问题切入点 在学习 Thread线程利用ThreadLocalMap实现线程的本地内存(变量副本)的时候&…...
arduino程序-MC猜数字5、6(基础知识)
arduino程序-MC猜数字5、6(基础知识) 1-23 MC猜数字-5 自定义函数自定义函数自定义清理显示内容函数displayClear()带参数函数displayNumber带参数、返回值的函数 1-24 MC猜数字-6 完成制作显示0~9数字函数改造产生随机数字函数改…...
【笔记】如何在ps里调整贴图
如图我想要在ps里把角上半部分画成绿色 1.打开ps,拖进贴图 2.把所有图层全选复制,除了实体、选择、背景和uv图层 3.把这些图层合并 4.自己手动选上半部分角 按shift加选 alt减选 5. 这里可以调整饱和度、色相、明度 6. 选好之后用羽化之后边缘会…...
【C++11】深度解析--异步操作(什么是异步?异步有那些操作?异步操作有什么用呢?)
目录 一、前言 二、什么是异步操作呢? 🔥异步的概念🔥 🔥异步的生活案例说明🔥 三、异步有那些操作呢? 🔥std::future🔥 💢std::future 的概念💢 &a…...
PHP苹果 V X iPhone微商i o s多分开V X语音转发密友朋友圈一键跟圈软件
苹果VX神器!iPhone微商必备:ios多开、VX语音转发、密友朋友圈一键跟圈软件大揭秘! 一、iOS多开新境界,工作生活两不误! 你是不是也烦恼过,想要在工作号和生活号之间自由切换,却因为iPhone的限制…...
LDR6020 iPad皮套一体式键盘充电方案解析
在移动办公与学习的浪潮中,iPad凭借其强大的性能与便携性,成为了越来越多人的首选设备。然而,随着工作与学习任务的日益复杂,单一的触控操作已难以满足高效、精准的需求。因此,搭配一款优秀的键盘成为了提升iPad使用体…...
网络舆情分析师证书/seo免费入门教程
str str.replace(/,/g, "");...
个人网站建设与维护/推广一般收多少钱
简介 为了方便开发人员可视化配置gpio,MTK提供了DCT工具,全称是Driver Customization Tool,该工具导入dws文件来产生驱动代码,它是个exe可执行程序,目前只支持在windows下运行,在ubuntu下运行可借助于wine…...
怎样建立自己的销售网站/今天的新闻 最新消息
随着汽车行业的发展,现在的纯电动车已经是值得所有消费者,认真考虑的购车选项了。如果说在2019年之前的新能源汽车市场还具有较为明显的政策引导色彩,部分消费者买电动车或者是为了占号、或是购车有优惠。但从去年到今年,越来越多…...
企梦云网站建设/成都最新数据消息
Java 抽象类 // 1.定义抽象类c1ass关键字前边添加 abstract // 2.抽象类是不能够被实例化的 // 3.在抽象类中可以定义一些子类公共的方法或属性 // 4.抽象方法只在抽象类中,提供声明,不需要实现,起到了一个强制的约束作用,要求子类必须实现 // 5.在抽象类中定义抽象方法 在方法…...
专业做外挂的网站/百度信息流广告投放
版本信息:SDE9.3、ORACLE11g1、SDE锁表检查处理1.1、查询并删除锁表进程(以sys或者system用户运行)select t1.username, t.object_id, t.session_id, t1.serial#,t.oracle_username,t.os_user_name, t.process from v$locked_object t,v$session t1 where t.session…...
网站建设活动海报/成都seo排名
创造者的思维方式 我不知道人们是怎么回事,缺乏想象力还是怎么的,所以我跟其他人对话常常遇到类似的问题。 我:A其实不怎么好。 其他人:你说A不好,难道你要我用B? (对于政治爱好者,如…...