前景提要
公元2020年9月,无线投屏手机端正在如火如荼的进行。大部分流程是复用接收端的,加上Windows组的小伙伴很稳,开发进度非常顺利,投屏、反控的主流程基本走完。例会上遗留了一个声音暂时播放不出来的问题,故事也由此展开…
情景再现
Chapter 1
A:声音播放不出来应该是小问题吧,估计哪里开关没开或者配置不对。
B:应该是吧,等下排查一下。
获得成就:Flag creator
怀疑点:
1. 调用流程有误
2. 设置的解码参数手机不兼容
因为解码播放是研究院做的,且基本流程是复用接收端的,流程也简洁明了。而且反向画面也正常显示了,反复对比参数并无区别,手机运行接收端程序,也能正常播放声音。
—> 故以上猜测均排除
眉头一皱,发现问题并没有那么简单。为了排查,控制变量,将此部分从项目里抽离,写demo进行验证。
Chapter 2
怀疑转移:
1. 静态库、动态库?
2. 动态注册、静态注册?
3. 编译版本不同?
曙光乍现:
-
确实静态库可行,但是这条路不行,抛一个小彩蛋吧。
—> 虽然后面了解链接、装载,符号解析原理后,其实可以尝试规避。
而且没找到根本原因替换成静态库,设置为File模式播放,黑屏,看index卡在nVidTime=10651, nFrameTime:10583(虽然我需要用的是流模式,但万一有其他暗坑) -
经由反复试错后发现竟然是JNI_OnLoad影响的,只要不加上JNI_OnLoad就行
本着解决优先的角度,批量替换成静态注册,但是遇到一个问题:
不写JNI_OnLoad,想在C++调用Java怎么办?怎么获取JavaVM?
1. JNI_OnLoad里直接赋值(当时就是卡在不能调用JNI_OnLoad)
2. JNI_CreateJavaVM:测试发现,这个方法已经不能用了
3. 通过JNI_Env获取JavaVM:int vmResult = env->GetJavaVM(&g_VM) --> 可行
Chapter 3
揭晓答案:最终发现只要在Java代码里,显示的执行 System.loadLibrary(“PlayCtrl”) ,不能播放声音的问题即可迎刃而解。
总结
JNI不难,但是遇到问题,如果不知道原理,举步维艰。
先康康 System.loadLibrary() 到底做了什么?
深入理解 System.loadLibrary
然后自然会想到:像stl基础的链接库,加载顺序是什么?先系统还是先应用?
很快啊,拿起《程序员的自我修养》,打出了一套链接、装载组合拳。
—> 工作后看的第一本C++书,因为被坑过,所以看的酣畅淋漓,暂时理解不够,后面按自己的理解总结一波,抛砖引玉
扩展思考
其实编译版本区别导致运行时出问题的现象,应该是相对比较少见的,毕竟你做项目的时候集成库的时机别人一般不会给你特别老的版本。且在Linux上可使用如下指令验证,无限投屏包括自己编的和sdk提供的20多个链接库,基本上版本都是4.9+
strings *.so |grep GLIB
strings *.so |grep GCC
彩蛋:
动态库、静态库混用导致动态指针转换失败:
详情:播放库提供的库是静态库,且用的是gnustl,网络库用的是libc++_shared,在网络库使用到dynamic_pointer_cast的时候出错了。
看看官网的建议:
1. 不再支持gnustl
2. 一个应用不得使用多个 C++ 运行时。不同的 STL 互不兼容
遗留问题:
看笔记,因动静态还触发过一次ANR问题(但真的忘记具体是啥错误了)