跨平台xamarin.Android 开发之 :适配各架构(X86_64 、 X86、arm64-v8a、 armeabi-v7a )FFmpeg注册

发布时间 2023-08-13 15:28:23作者: TanZhiWei

从事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>

注: X86_64 、arm64-v8a、  armeabi-v7a 编解码有细微差异:体现在内存分配以及取指操作,有时间再补一篇针对讲解