问题背景
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,