问题背景

android10默认执行文件沙箱机制,native层代码失去了通过文件路径访问公共媒体文件的权限。当时可通过android:requestLegacyExternalStorage="true" 来兼容,设置这个标志后依然可以通过路径访问。

估计是谷歌考虑到不太合理,android11改回来了,android11的真机上native层恢复可以通过路径访问公共媒体文件,不需要设置android:requestLegacyExternalStorage="true"

但是蛋疼的是,发的目标android11的包还是有可能被android10真机下载,所以android:requestLegacyExternalStorage="true"这个还是得设置以免android10的用户出错。

另外,发包带上android:requestLegacyExternalStorage="true"这个标志还会收到谷歌的警告。

基于以上麻烦,决定试试使用文件描述符来代替路径。在java层通过MediaStore的权限打开媒体文件,取得文件描述符,然后把文件描述符传到native层,native层使用文件描述符来读写文件。这样就可以把android:requestLegacyExternalStorage="true"甩掉。

解决思路

ffmpeg只支持路径参数的api,我们需要想办法让他支持文件描述符(FD)。

一种方法是重写新的支持FD的api,这样修改太大,最后放弃这种做法。

看了一下ffmpeg内部,关于文件操作的部分,在AVformat模块里面。刚好使用的文件api不是fopen这套,是系统io这套(open,read),也就是说文件描述符只要传进去就能直接用。

想了一番后,决定不需要大改,把FD有效传进去就行了。而FD是个int值,可以转成字符串送进去,在里面再恢复int,这样利用现有的路径就能带进去。

为了区别真正的路径,我们选个假路径作为沟通的“密码”就行了。

"fileDescriptorkey/" + FD

例如在外面avformat_open_input传 “fileDescriptorkey/123” 进去,里面检测到fileDescriptorkey这串“密码”,就知道这不是普通路径,是带货来的,后面跟的123就是FD值。

接下来就是修改ffmpeg里面的代码,识别到这个key就另外处理就行了。

ffmpeg修改

跟踪一下代码,确认里面关于路径逻辑修改后,不影响整个解码流程走向,确实可行。
而最后定位到只要修改avformat模块下的file.c文件就可以打到我要的效果。这个文件刚好负责全部的普通文件处理,没有什么耦合性,容易修改。
改两个函数,添加一个函数,就行,如下

static int getfdfs(const char *str,char *ptr)
{while (*str != '.' && *str != 0) {*ptr = *str;ptr++;str++;}*ptr = 0;return 0;
}

static int file_check(URLContext *h, int mask)
{int ret = 0;const char *filename = h->filename;av_strstart(filename, "file:", &filename);char *fds;char fdss[64] = { 0 };if (av_strstart(filename, "fileDescriptorkey/", &fds)) {int fd;getfdfs(fds, fdss);fd = atoi(fdss);if (fd != -1) {#if         HAVE_FCNTLint flag = 0;flag = fcntl(fd, F_GETFL);if (flag != -1) {int accmode;accmode = flag & O_ACCMODE;if (accmode == O_RDONLY)ret |= mask&AVIO_FLAG_READ;else if (accmode == O_WRONLY)ret |= mask&AVIO_FLAG_WRITE;else if (accmode == O_RDWR){ret |= mask&AVIO_FLAG_READ;ret |= mask&AVIO_FLAG_WRITE;}}else {av_log(h, AV_LOG_WARNING, "ttflog file_check cannot fcntl\n");return AVERROR(errno);}
#elseret |= mask&AVIO_FLAG_READ;
#endif}else {av_log(h, AV_LOG_WARNING, "ttflog file_check fd<=0 %d\n",fd);}}else{#if HAVE_ACCESS && defined(R_OK)if (access(filename, F_OK) < 0)return AVERROR(errno);if (mask&AVIO_FLAG_READ)if (access(filename, R_OK) >= 0)ret |= AVIO_FLAG_READ;if (mask&AVIO_FLAG_WRITE)if (access(filename, W_OK) >= 0)ret |= AVIO_FLAG_WRITE;
#elsestruct stat st;
#   ifndef _WIN32ret = stat(filename, &st);
#   elseret = win32_stat(filename, &st);
#   endifif (ret < 0)return AVERROR(errno);ret |= st.st_mode&S_IRUSR ? mask&AVIO_FLAG_READ  : 0;ret |= st.st_mode&S_IWUSR ? mask&AVIO_FLAG_WRITE : 0;
#endif}return ret;
}

static int file_open(URLContext *h, const char *filename, int flags)
{FileContext *c = h->priv_data;int access;int fd;struct stat st;char *fds;av_strstart(filename, "file:", &filename);if (av_strstart(filename, "fileDescriptorkey/", &fds)) {char fdss[64] = { 0 };getfdfs(fds, fdss);fd = atoi(fdss);av_log(h, AV_LOG_WARNING, "ttflog fileDescriptor-key %d\n",fd);//if (fd <= 0) {//  return -1000;//}//if (fcntl(fd, F_GETFD, 0)) {//}if (fstat(fd, &st) != 0) {av_log(h, AV_LOG_WARNING, "ttflog cannot fstat\n");return AVERROR(errno);}else {h->is_streamed = S_ISFIFO(st.st_mode);av_log(h, AV_LOG_WARNING, "ttflog is_streamed %d\n", h->is_streamed);}}else {if (flags & AVIO_FLAG_WRITE && flags & AVIO_FLAG_READ) {access = O_CREAT | O_RDWR;if (c->trunc)access |= O_TRUNC;}else if (flags & AVIO_FLAG_WRITE) {access = O_CREAT | O_WRONLY;if (c->trunc)access |= O_TRUNC;}else {access = O_RDONLY;}
#ifdef O_BINARYaccess |= O_BINARY;
#endiffd = avpriv_open(filename, access, 0666);if (fd == -1)return AVERROR(errno);h->is_streamed = !fstat(fd, &st) && S_ISFIFO(st.st_mode);}c->fd = fd;/* Buffer writes more than the default 32k to improve throughput especially* with networked file systems */if (!h->is_streamed && flags & AVIO_FLAG_WRITE)h->min_packet_size = h->max_packet_size = 262144;return 0;
}

java层示例

获取FD组成串

pfd = this.getContentResolver().openFileDescriptor(uri, "r");if (pfd != null) {//Bitmap bitmap = BitmapFactory.decodeFileDescriptor(pfd.getFileDescriptor());int fd = pfd.detachFd();path = "fileDescriptorkey/" + fd;

把这个path放avformat_open_input就可以了。

补充

可以加个“.mp4”之类指定一下文件类型,记忆中ffmpeg里面好像有根据文件后缀的逻辑判断。没仔细看。
我目前只验证了读权限没问题,写没用到
如果确认不会用到seek操作,使用pipe也是可以的,这样不用改ffmpeg

修改ffmpeg支持文件描述符,以适配android10沙箱机制相关推荐

  1. linux文件描述符导致squid拒绝服务

    linux文件描述符导致squid拒绝服务   前几天因工作需要在RHEL4.8上面架设了一个squid双网代理,刚开始测试一切正常,然后就在前台负载均衡服务器把这个代理地址加上,运行一段时间后,客服 ...

  2. select中文件描述符上限为什么是1024?

    一.两个1024 select中存放文件描述符的数组大小FD_SETSIZE为1024 进程的文件描述符上限默认是1024,正是因为这个原因,select设计时才把数组大小设计为1024 二.问题来了 ...

  3. Linux网络编程(六)-高并发服务器03-I/O多路复用03:epoll【红黑树;根节点为监听节点】【无宏FD_SETSIZE限制;不需每次都将要监听的文件描述符从应用层拷贝到内核;不需遍历树】

    一.epoll概述 epoll的本质是一个[红黑树].监听结点为根节点. 大量并发,少量活跃效率高. epoll是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并 ...

  4. Linux下的文件描述符

    引文 在 Linux 的世界里,一切设备皆文件.对文件的操作都是通过文件描述符(fd)来进行的. Linux 中有7种文件类型: 文件类型 文件类型描述 符号 普通文件 最常使用的一类文件,其特点是不 ...

  5. linux字符修改时间,字符集修改、Linux时间同步、调整文件描述符

    字符集 UTF-8:非定长,1-4字节,广泛支持,应用最广 GBK:定长,双字节,不是国际标准,支持的系统少 [root@wuyike ~]# cat /etc/sysconfig/i18n LANG ...

  6. Linux服务器文件描述符最大值修改

    文件描述符的有效范围是 0 到 OPEN_MAX.Linux 2.4.22 强制规定最多不能超过 1,048,576 . 文件描述符是由无符号整数表示的句柄,进程使用它来标识打开的文件.文件描述符与包 ...

  7. linux命令之修改系统允许进程打开文件描述符限制-ulimit

    在linux下网络服务经常会遇到的一个问题就打开过多的端口或者创建大量的连接会失败,这是因为系统对每个进程能够打开的资源是有限制的. 打开过多的端口或者创建大量的连接,都会打开大量文件描述符,linu ...

  8. linux修改文件描述符,linux最大允许的文件描述符open files数nofile修改

    open file resource limit 是linux中process可以打开的文件句柄数量.增加这个数值需要调整两个配置: 第一步, 修改系统最大允许的文件描述符 查看当前的设置: $ ca ...

  9. 查看、修改linux系统的最大链接数限制、文件描述符限制、端口范围限制、虚拟内存等...

    一.修改最大连接数 1.查看当前文件描述符的限制数目的命令: ulimit -n 2.修改文件描述符的限制数目 2.1 临时改变当前会话: ulimit -n 65536 2.2 永久变更需要下面两个 ...

最新文章

  1. 进阶学习(3.2)Factory Method Pattern 工厂方法模式
  2. 剑指Offer之栈的压入、弹出序列
  3. 理解SQLNET.AUTHENTICATION_SERVICES参数|转|
  4. python中双冒号_c++中冒号(:)和双冒号(::)的用法和c/c++ 位域结构体
  5. 对传统视觉惯性的颠覆
  6. oracle长连接超时设置
  7. unity导入素材时材质丢失素材变成粉红色的解决方法
  8. t3修改计算机后就无法登录了,电脑更换系统后,用友T3登录不上了,一直显示这个,怎么处理,这个怎么解决...
  9. Git的GUI工具sourcetree的使用
  10. Linux系统编程:pipe匿名管道的使用,实现linux命令下管道命令
  11. springboot增删改查案例_大神基于SpringBoot开发一个Restful服务,实现增删改查功能...
  12. 设计模式--代理模式Proxy(结构型)
  13. 远程工作两个月的体会(转)
  14. hive3.1.2 分布式安装 (基于hadoo3.1.3+spark)
  15. 产品活跃度做好很难?提高用户活跃度用对方法一点也不难
  16. 平面几何----三割线定理引理的证明
  17. sencha app watch php,Sencha Cmd使用指南
  18. 阿里大数据产品Dataphin上线公共云,将助力更多企业构建数据中台
  19. 想写一个供教育培训机构排课和教师管理的软件
  20. WIN10系统从睡眠状态唤醒后电脑变卡顿

热门文章

  1. chrome设置默认首页无效
  2. 将一个字符串转换为对应的整数
  3. 相册里的WEB3 Festivals香港记忆
  4. 藏苹果 HNSUT 1889
  5. Android studio配置代理下载依赖
  6. 漫画 | Code Review快把我逼疯了!
  7. HTML爱心代码超好看
  8. UTC与BJT时间换算C语言
  9. 系统桌面常见问题处理
  10. 部署: 搭建 Apache RocketMQ 单机环境与Rocketmq-console