家里有个海康的网络摄像机,虽然有手机app可以远程访问,但是不方便定制。了解到海康提供有网络sdk,,接口功能丰富且强大。正好手边有全志的okt507开发板闲置,周末可以搞些事情玩。但是竟发现海康提供的arm64平台下的sdk有这等问题,印象中海康大公司,实力还不错,这太不应该了吧。
海康网络sdk下载链接:海康开放平台
海康设备网络SDK是专为海康威视(Hikvision)的硬件产品设计的一款重要组件,尤其针对arm架构的设备。这个SDK的核心功能在于实现了设备私有网络通信协议,使得开发者能够轻松地进行远程访问和控制海康威视的各种设备,如监控摄像头、NVR等。该SDK为软件的二次开发提供了便利。
问题描述
在使用设备网络SDK_ArmLinux64 这套SDK时,发现其存在问题:视频渲染库 libAudioRender.so 在嵌入式linux平台上无法使用。经查证,发现原因是因为:
该库依赖使用了OpenGL,导致编译不过,更是无法运行。在嵌入式平台上,使用的应是OpenGL ES,而非OpenGL。海康的嵌入式产品和人才这么多,出现这等错误!不应该吧。
OpenGL ES是OpenGL的一个子集,专门针对嵌入式系统进行了优化。它移除了OpenGL 中一些低效能的操作方式,专注于提供高性能的图形渲染能力。因此,在ARM的嵌入式Linux上,常用且被支持的是OpenGL ES。OpenGL则常用在PC端。
我在全志的T507平台下,编译报如下错误:
提示找不到libGL.so.1和libX11.so.6
全志T507支持OpenGL ES 3.2/2.0/1.0
即便像RK3588这些高端点的带GPU的嵌入式ARM芯片,也都是使用OpenGL ES。而海康提供的这套针对ARM平台的SDK,其中的视频渲染模块,竟直接使用了OpenGL ,导致在嵌入式linux平台下无法使用。
查看下该库的依赖:
尝试解决办法
如果libSuperRender.so库中使用的OpenGL 的接口,跟OpenGL ES的接口一致的话,则可以通过工具修改库的链接的方式实现。
先查看下该库依赖了哪些链接:
readelf -d libSuperRender.so |grep -i NEED
上面这个readelf命令,即便 libSuperRender.so是arm64体系结构上的库,在x86下依然可以使用readelf查看。
还可以查看该函数的定义,使用objdump命令(但此命令则只能在板子上看了,在x86的linux系统上无法查看arm64体系结构下的so库),输入以下命令:
objdump -D xxx.so | grep 函数名
或者使用交叉编译工具链自带的aarch64-linux-gnu-objdump和aarch64-linux-gnu-nm来查看arm64体系结构上的库的依赖和函数名。
替换libSuperRender库依赖
patchelf
是一个用于修改 ELF(Executable and Linkable Format)格式文件的工具,它可以改变可执行文件或共享库的属性,如动态链接器(interpreter)、RPATH、SONAME 等。以下是一些基本的 patchelf
用法:
安装 patchelf
:
sudo apt-get install patchelf
查看当前设置的动态链接器:
patchelf --print-interpreter your_file
查看依赖库:
patchelf --print-needed libSuperRender.so
添加依赖库:
patchelf --add-needed libfoo.so.1 my-program.so
替换依赖库:
patchelf --replace-needed liboriginal.so.1 libreplacement.so.1 my-program.so
移除依赖库:
移除特定依赖库:
patchelf --remove-needed libfoo.so.1 my-program.so
修改 RPATH:
设置 RPATH:
patchelf --set-rpath /opt/my-libs/lib:/other-libs my-program
移除 RPATH:
patchelf --remove-rpath my-program
缩减 RPATH,只保留必要的路径:
patchelf --shrink-rpath my-program
修改 SONAME:
查看 SONAME:
patchelf --print-soname your_file
设置 SONAME:
patchelf --set-soname libnewname.so.3.4.5 path/to/libmylibrary.so.1.2.3
关于RPATH
RPATH(Runtime PATH)是一种在Linux系统中用于动态链接的机制,它是动态链接器(dynamic linker)在运行时搜索动态库的路径列表。RPATH 是在编译动态库或者可执行文件时设置的,并且被嵌入到这些文件中。
RPATH 的用途
-
确定库搜索路径:当一个可执行文件运行时,动态链接器需要找到它依赖的动态库。RPATH 提供了一组预先定义的目录,动态链接器会在这些目录中搜索所需的库。
-
安全性:通过设置 RPATH,可以防止运行时对库文件的“篡改”。例如,如果你的应用程序依赖于特定版本的库,你可以将 RPATH 设置为包含这些特定版本库的目录,这样即使系统中安装了其他版本的库,应用程序也会使用指定版本的库。
-
便携性:RPATH 允许应用程序打包它们需要的库,并且可以在没有安装这些库到系统的情况下运行。这对于分发独立的应用程序或者在不同环境中迁移应用程序非常有用。
-
灵活性:RPATH 可以被设置为相对路径或者绝对路径。使用相对路径可以提高应用程序的灵活性,因为应用程序可以随着库的移动而移动,而不需要重新配置路径。
设置 RPATH
RPATH 可以通过以下几种方式设置:
-
编译时设置:在编译可执行文件时,可以使用链接器选项(如
-rpath
)来设置 RPATH。 -
安装时设置:在安装应用程序时,可以使用
patchelf
工具或者chrpath
工具来修改已经编译好的可执行文件的 RPATH。 -
运行时设置:也可以在运行时通过环境变量
LD_LIBRARY_PATH
来临时覆盖 RPATH 的设置。
示例:
假设你有一个可执行文件 myapp
,它依赖于 libmylib.so
库,你可以在编译 myapp
时设置 RPATH 如下:
gcc myapp.c -o myapp -L/path/to/lib -Wl,-rpath,/path/to/lib -lmylib
这里 -Wl,-rpath,/path/to/lib
选项告诉链接器将 /path/to/lib
目录嵌入到 myapp
的 RPATH 中。
然后,当你运行 myapp
时,动态链接器会在 /path/to/lib
目录下查找 libmylib.so
。
RPATH 是一个强大的工具,可以帮助确保你的应用程序能够找到正确的库版本,并且可以在多种环境中无缝运行。
RPATH 和 LD_LIBRARY_PATH区别
RPATH
和 LD_LIBRARY_PATH
都是Linux系统中用于指定动态链接库搜索路径的机制,但它们在应用方式和作用范围上有所不同:
-
RPATH:
- 编译时设置:RPATH是在编译或创建可执行文件或共享库时嵌入到文件本身的。
- 持久性:RPATH的设置是持久的,即使环境变量
LD_LIBRARY_PATH
发生变化,RPATH中指定的路径也会被首先搜索。 - 安全性:使用RPATH可以避免运行时对库文件的篡改,因为它是嵌入到二进制文件中的,不容易被外部环境影响。
- 范围:RPATH只影响包含它的可执行文件或共享库。
-
LD_LIBRARY_PATH:
- 环境变量:
LD_LIBRARY_PATH
是一个环境变量,它在运行时由shell设置,用于临时改变动态链接器的库搜索路径。 - 非持久性:
LD_LIBRARY_PATH
的设置是非持久的,它只在当前shell会话中有效,关闭终端或重启系统后设置会失效。 - 灵活性:可以在不重新编译应用程序的情况下,通过改变
LD_LIBRARY_PATH
来控制应用程序使用的库版本。 - 全局性:
LD_LIBRARY_PATH
影响当前用户的所有进程,而不仅仅是设置该环境变量的应用程序。
- 环境变量:
使用场景:
-
使用RPATH:
- 当你想让应用程序独立于系统库,或者确保应用程序总是使用特定版本的库时。
- 在分发应用程序时,确保用户不需要额外配置环境变量就能运行应用程序。
-
使用LD_LIBRARY_PATH:
- 当你需要临时使用不同版本的库进行测试或调试时。
- 当你想要覆盖RPATH设置,或者运行时动态决定库的搜索路径时。
示例:
-
设置RPATH:
gcc -o myapp myapp.c -L/path/to/libs -Wl,-rpath,/path/to/libs -lmylib
这里,
-Wl,-rpath,/path/to/libs
选项将/path/to/libs
目录嵌入到myapp
的RPATH中。 -
设置LD_LIBRARY_PATH:
export LD_LIBRARY_PATH=/path/to/libs:$LD_LIBRARY_PATH ./myapp
这里,通过
export
命令设置环境变量LD_LIBRARY_PATH
,使得动态链接器在搜索库时也会查看/path/to/libs
目录。
总的来说,RPATH提供了一种更为稳定和持久的库搜索路径设置方式,而LD_LIBRARY_PATH
则提供了一种灵活的、临时的设置方式。
问题解决
按上述方法尝试,问题解决了吗?遗憾的是依然没有。在不修改libSuperRender.so
库源码的情况下,这个问题无解。因为它源码使用了X Window System 的 OpenGL 扩展接口,依赖和使用了X11.so库。从glXGetCurrentDrawable函数可以看出:
glXGetCurrentDrawable
是一个函数,它返回当前的 drawable,这个 drawable 是通过 glXMakeCurrent
指定的。如果没有当前 drawable,它将返回 None
。这个函数返回的是客户端信息,不会向服务器发送请求。
这个函数是 GLX 库的一部分,GLX 是用于 X Window System 的 OpenGL 扩展。GLX 允许 OpenGL 程序在 X Window System 上创建窗口和渲染离屏缓冲区。glXGetCurrentDrawable
函数通常用于确定当前的绘图表面,例如窗口或像素缓冲区。
如果你需要在你的程序中使用 glXGetCurrentDrawable
,你需要确保你的程序链接了 GLX 库。在 Linux 系统上,这通常意味着需要必须要链接到 libGL.so
库。
最后的解决办法,修改官方提供的QtDemo源码(基于QT4.7的),一方面把其改造为QT5的工程项目,一方面去掉关于视频播放部分的接口调用,去除-lPlayCtrl -lSuperRender这两个库的链接,这两个库在嵌入式linux平台下就是个废柴,用不了。希望贵公司能够修复这个bug。
其他资源
https://zhuanlan.zhihu.com/p/362149017
windows和Linux下查看动态库dll/so的函数接口_查看so文件导出的接口-CSDN博客
海康开放平台