跨平台xamarin.Android 开发之 :适配架构(X86_64 、arm64-v8a、 armeabi-v7a )FFmpeg注册
从事Windows,项目探索预研跨平台开发,对Android 只知道有X86_64 、 X86、arm64-v8a、 armeabi-v7a这么个东西其他空白。编译入手采用Xamarin.Android 开发。
通过摸索。在Xamarin.Android中使用FFmpeg 编解码,需要获取源码编译成对应Android 架构的so动态库,如何编译不在此处讨论,稍后补一篇专门对编译成对应Android 架构的so动态库讲解。
1 需要在Android 中获取我们存放好的第三方.so 动态,在Android 中有这么一个文件夹 Assets,并将库生成操作改成AndroidAsset。
那么在代码当中我们就可以使用Android.App.Application.Context.Assets.Open(FileName)读取
注:上面有一个区别,在X86_64 架构中,使用FFmpeg的动态库不需要"libc++_shared.so",因为系统自带。在ARM 中就需要在官网当中去下载对应32、64 的"libc++_shared.so"。
2 x64_64 下使用注册FFmpeg动态库,动态库的注册实现,此处不加以讲解,稍后在博客中针对展开讲解。
/// <summary> /// 注册FFMPEG /// </summary> /// <param name="ffmpegAndroidDlls"></param> private void InitFFmpeg() { //安卓依赖动态库 Var _ffmpegAndroidDlls = new List<string> { "libavcodec.so.60", "libavdevice.so.60", "libavfilter.so.9", "libavformat.so.60", "libavutil.so.58", "libswresample.so.4", "libswscale.so.7" }; //无论在Release还是Debug环境下 string assemblyPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal); foreach (var ffmpegDll in ffmpegAndroidDlls) { var assemblyDirectoryDll = System.IO.Path.Combine(assemblyPath, ffmpegDll); using var inputStream = Android.App.Application.Context.Assets.Open($"{cpuArchitecture}/{ffmpegDll}"); using var outputStream = new FileStream(assemblyDirectoryDll, FileMode.Create, FileAccess.Write); inputStream.CopyTo(outputStream); } //设置库路径 if (!Directory.Exists(assemblyPath)) { throw new DirectoryNotFoundException("FFMPEG 动态库路径不存在!"); } FFmpegWrapper.RegisterFFmpeg(assemblyPath); }
3 X86_64调用上面注册即可。针对ARM 架构情况,就需要添加"libc++_shared.so"了。
/// <summary> /// 注册FFMPEG /// </summary> /// <param name="ffmpegAndroidDlls"></param> private void InitFFmpeg() { //安卓依赖动态库 Var _ffmpegAndroidDlls = new List<string> { "libavcodec.so.60", "libavdevice.so.60", "libavfilter.so.9", "libavformat.so.60", "libavutil.so.58", "libswresample.so.4", "libswscale.so.7" }; var cpuArchitecture = Android.OS.Build.CpuAbi; Debug.WriteLine($"当前Android Cpu架构: {cpuArchitecture}"); if (cpuArchitecture.Equals("arm64-v8a") || cpuArchitecture.Equals("armeabi-v7a")) { ffmpegAndroidDlls.Add("libc++_shared.so"); } //无论在Release还是Debug环境下 string assemblyPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal); foreach (var ffmpegDll in ffmpegAndroidDlls) { var assemblyDirectoryDll = System.IO.Path.Combine(assemblyPath, ffmpegDll); using var inputStream = Android.App.Application.Context.Assets.Open($"{cpuArchitecture}/{ffmpegDll}"); using var outputStream = new FileStream(assemblyDirectoryDll, FileMode.Create, FileAccess.Write); inputStream.CopyTo(outputStream); } //设置库路径 if (!Directory.Exists(assemblyPath)) { throw new DirectoryNotFoundException("FFMPEG 动态库路径不存在!"); } FFmpegWrapper.RegisterFFmpeg(assemblyPath); }
4 【一落千丈】信心满满,在同事的最新Android手机上注册测试也通过了。后来同事给了一部2019年的老Android 10. 测试挂了
异常: [linker] library "/data/user/0/com.companyname.screenshareandroid/files/libavutil.so.58" ("/data/data/com.companyname.screenshareandroid/files/libavutil.so.58") needed or dlopened by "(unknown)" is not accessible for the namespace: [name="(anonymous)", ld_library_paths="", default_library_paths="/data/app/com.companyname.screenshareandroid-cTWv_NzZPbV7XkZ9G2wXbg==/lib/arm64:/data/app/com.companyname.screenshareandroid-cTWv_NzZPbV7XkZ9G2wXbg==/base.apk!/lib/arm64-v8a", permitted_paths=""]
这一下整不会了。然后请教Android 应用,Android 应用同事表示没见过,接着请教Android 系统,Android 系统同事表示也没见过。这个就尴尬了,没见过也正正常,毕竟我们使用C# xamarin 对Android 开发,不是原生的Android 应用开发。现在的问题是怎么处理。
5 查找资料,查看Xamarin 官方文档, 这一句 default_library_paths="/data/app/com.companyname.screenshareandroid-cTWv_NzZPbV7XkZ9G2wXbg==/lib/arm64:/data/app/com.companyname.screenshareandroid-cTWv_NzZPbV7XkZ9G2wXbg==/base.apk!/lib/arm64-v8a" 很重要。意思是在查找动态库的默认路径是 虚拟路径"/data/app/com.companyname.screenshareandroid-cTWv_NzZPbV7XkZ9G2wXbg==/lib/arm64:/data/app/com.companyname.screenshareandroid-cTWv_NzZPbV7XkZ9G2wXbg==/base.apk!/lib/arm64-v8a"。这个虚拟路径是什么? 就是Apk的内部路径/lib/arm64-v8a。好找到这个信息就好办了。
6 解决上面问题,通过查阅文档知道在Android 下添加jniLibs文件夹,然后添加对应架构的文件夹目录,把动态库放到对应架构目录下面,生成操作设置成AndroidNativeLibrary
或则修改Android.csproj
<ItemGroup> <AndroidNativeLibrary Include="jniLibs\arm64-v8a\libavcodec.so.60"/> <AndroidNativeLibrary Include="jniLibs\arm64-v8a\libavdevice.so.60" /> <AndroidNativeLibrary Include="jniLibs\arm64-v8a\libavfilter.so.9" /> <AndroidNativeLibrary Include="jniLibs\arm64-v8a\libavformat.so.60" /> <AndroidNativeLibrary Include="jniLibs\arm64-v8a\libavutil.so.58" /> <AndroidNativeLibrary Include="jniLibs\arm64-v8a\libc++_shared.so" /> <AndroidNativeLibrary Include="jniLibs\arm64-v8a\libswresample.so.4" /> <AndroidNativeLibrary Include="jniLibs\arm64-v8a\libswscale.so.7" /> <AndroidNativeLibrary Include="jniLibs\armeabi-v7a\libavcodec.so.60" /> <AndroidNativeLibrary Include="jniLibs\armeabi-v7a\libavdevice.so.60" /> <AndroidNativeLibrary Include="jniLibs\armeabi-v7a\libavfilter.so.9" /> <AndroidNativeLibrary Include="jniLibs\armeabi-v7a\libavformat.so.60" /> <AndroidNativeLibrary Include="jniLibs\armeabi-v7a\libavutil.so.58" /> <AndroidNativeLibrary Include="jniLibs\armeabi-v7a\libc++_shared.so" /> <AndroidNativeLibrary Include="jniLibs\armeabi-v7a\libswresample.so.4" /> <AndroidNativeLibrary Include="jniLibs\armeabi-v7a\libswscale.so.7" /> <AndroidNativeLibrary Include="jniLibs\x86_64\libavcodec.so.60" /> <AndroidNativeLibrary Include="jniLibs\x86_64\libavdevice.so.60" /> <AndroidNativeLibrary Include="jniLibs\x86_64\libavfilter.so.9" /> <AndroidNativeLibrary Include="jniLibs\x86_64\libavformat.so.60" /> <AndroidNativeLibrary Include="jniLibs\x86_64\libavutil.so.58" /> <AndroidNativeLibrary Include="jniLibs\x86_64\libswresample.so.4" /> <AndroidNativeLibrary Include="jniLibs\x86_64\libswscale.so.7" /> </ItemGroup>
1 在这里需要注意一点,在Release模式下需要关闭性能优化,否则不会拷贝过去
2 注意:在真机打包apk时,需要去掉.so 后面的版本号,否则在安装Apk的时候不会将库拷贝到/data/app/lib 下
/// <summary> /// 注册FFMPEG /// </summary> /// <param name="ffmpegAndroidDlls"></param> private void InitFFmpeg() { //安卓依赖动态库 string path = Android.App.Application.Context.ApplicationInfo.NativeLibraryDir; FFmpegWrapper.RegisterFFmpeg(assemblyPath); }
查看API已经打进包了。为什么Android 11 以上不用那么操作。在Android 11 以后对路径开启了兼容操作
注: X86_64 、arm64-v8a、 armeabi-v7a 编解码有细微差异:体现在内存分配以及取指操作,有时间再补一篇针对讲解