前言 请注意:这是一个不成功的探索,仅供参考。
在做鸿蒙三方库移植时,一眼相中了目前还没有人移植的 vlc。
作为音视频、播放器领域巨头,vlc 的名号可谓无人不知、无人不晓。
但是,由于 vlc 使用的编译工具链过于古旧,项目庞大难以一一分析,本人对于三方库编译移植一条龙又是第一次接触。
凡此种种,不一而足。这些因素都指向了此次探索的失败结局。
解构 一切恐惧都来源于未知,对于 vlc 及其衍生物的不熟悉,不了解,片面理解导致了选型的失败。
从项目层级来看:
分析 从目标入手 由官方文档可知,vlc 目录下的每个模块含义
lib - libvlc 源码
src - libvlccore 源码
compat - libcompat 源码
include - 头文件
modules - 各种必要与非必要插件依赖
按照官方教程编译出来 libvlc.so 之后可以查看依赖关系
readelf -d libvlc.so
readelf -d libvlccore.so
直接看上去似乎只要编译生成 lib
和 src
对应的目标就可以了
回到配置文件 Makefile.am
和 configure.ac
是 autotools
工具链中的 autoMake
和 autoConfigure
两个工具的配置文件,二者会共同生成 configure
脚本文件。
而 configure
文件则可以生成 makefile
文件,以供编译。
configure.ac
文件中包含了各种变量、宏定义,其实这些都可以通过 configure
参数来调整,因此我们先不管它。
我们来看最重要的 Makefile.am
重点一共有两部分
第一部分是要配置的子文件夹,更改这个能够调整需要编译的文件夹
1 2 SUBDIRS = compat po share src modules lib doc bin test DIST_SUBDIRS = m4 $(SUBDIRS)
第二部分是依赖关系和目标,可以看到 libvlc -> libvlccore -> libcompat
更改这个能够调整编译目标,也告诉了我们可以在 make
时选择你所需要的目标
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 libcompat: cd compat && $(MAKE) $(AM_MAKEFLAGS) libvlccore: libcompat cd src && $(MAKE) $(AM_MAKEFLAGS) libvlccore.la libvlc: libvlccore cd lib && $(MAKE) $(AM_MAKEFLAGS) libvlc.la core: libvlc vlc$(EXEEXT) cd bin && $(MAKE) $(AM_MAKEFLAGS) vlc$(EXEEXT) vlc-static$(EXEEXT) doc: cd doc && $(MAKE) $(AM_MAKEFLAGS) doc .PHONY : libvlc core doc
我尝试过直接 make libvlc
会报
1 fatal error: 'fourcc_tables.h' file not found
注意他写的是 rebuild
,可能是你得先编译通过了才能这么弄
因此,我们可以确定编译目标了:
编译 compat src lib
文件夹中的目标
获取 libvlc.so
供进一步开发
编译 交叉编译工具 工欲善其事,必先利其器。
选择 lycium
作为交叉编译工具,因为它支持 configure
编译方式,而且不需要写 BUILD.gn
,也就不需要和源码一起编译。
选择这款工具会让你写一个 HPKBUILD
,如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 # This is an example HPKBUILD file. Use this as a start to creating your own, # and remove these comments. # NOTE: Please fill out the license field for your package! If it is unknown, # then please put 'unknown' .# Contributor: Your Name <youremail@domain.com> # Maintainer: Your Name <youremail@domain.com> pkgname=vlc # 库名 pkgver=3.0.20 # 库版本 pkgrel=0 # 发布号 pkgdesc="" # 库描述 url="" # 官网链接 archs=("armeabi-v7a", "arm64-v8a") # cpu 架构 license=() depends=() # 依赖库的目录名 必须保证被依赖的库的archs是当前库的archs的超集 makedepends=() # 构建库时的依赖工具->需要用户安装的工具 source="https://get.videolan.org/$pkgname/$pkgver/$pkgname-$pkgver.tar.xz" # 库源码下载链接 downloadpackage=true # 是否自动下载压缩包,如若不写默认 true. (应对一些特殊情况,代码只能 git clone (项目中依赖 submoudle )) autounpack=true # 是否自动解压,如若不写默认 true, 如果为 false 则需要用户在 prepare 函数中自行解压 buildtools="configure" # 编译方法, 暂时支持cmake, configure, make等, 是什么就填写什么. 如若不写默认为cmake. builddir=$pkgname-$pkgver # 源码压缩包解压后目录名 编译目录名 packagename=$builddir.tar.xz # 压缩包名 # source envset.shmyhost= # 为编译设置环境,如设置环境变量,创建编译目录等 prepare() { mkdir -p $builddir/$ARCH-build if [ $ARCH == ${archs[0]} ] then #setarm32ENV export AS=${OHOS_SDK}/native/llvm/bin/llvm-as export CC=${OHOS_SDK}/native/llvm/bin/arm-linux-ohos-clang export CXX=${OHOS_SDK}/native/llvm/bin/arm-linux-ohos-clang++ export LD=${OHOS_SDK}/native/llvm/bin/ld.lld export STRIP=${OHOS_SDK}/native/llvm/bin/llvm-strip export RANLIB=${OHOS_SDK}/native/llvm/bin/llvm-ranlib export OBJDUMP=${OHOS_SDK}/native/llvm/bin/llvm-objdump export OBJCOPY=${OHOS_SDK}/native/llvm/bin/llvm-objcopy export NM=${OHOS_SDK}/native/llvm/bin/llvm-nm export AR=${OHOS_SDK}/native/llvm/bin/llvm-ar export CFLAGS="-DOHOS_NDK -fPIC -D__MUSL__=1 -I${OHOS_SDK}/native/sysroot/usr/include" export CXXFLAGS="-DOHOS_NDK -fPIC -D__MUSL__=1 -I${OHOS_SDK}/native/sysroot/usr/include -g -O2" export LDFLAGS="-L${OHOS_SDK}/native/sysroot/usr/lib" export LIBS="$LIBS -lc" myhost=arm-rk3568-linux-gnu fi if [ $ARCH == ${archs[1]} ] then # setarm64ENV export AS=${OHOS_SDK}/native/llvm/bin/llvm-as export CC=${OHOS_SDK}/native/llvm/bin/aarch64-linux-ohos-clang export CXX=${OHOS_SDK}/native/llvm/bin/aarch64-linux-ohos-clang++ export LD=${OHOS_SDK}/native/llvm/bin/ld.lld export STRIP=${OHOS_SDK}/native/llvm/bin/llvm-strip export RANLIB=${OHOS_SDK}/native/llvm/bin/llvm-ranlib export OBJDUMP=${OHOS_SDK}/native/llvm/bin/llvm-objdump export OBJCOPY=${OHOS_SDK}/native/llvm/bin/llvm-objcopy export NM=${OHOS_SDK}/native/llvm/bin/llvm-nm export AR=${OHOS_SDK}/native/llvm/bin/llvm-ar export CFLAGS="-DOHOS_NDK -fPIC -D__MUSL__=1 -I${OHOS_SDK}/native/sysroot/usr/include" export CXXFLAGS="-DOHOS_NDK -fPIC -D__MUSL__=1 -I${OHOS_SDK}/native/sysroot/usr/include -g -O2" export LDFLAGS="-L${OHOS_SDK}/native/sysroot/usr/lib" export LIBS="$LIBS -lc" myhost=aarch64-rk3568-linux-gnu fi cd $builddir ./bootstrap cd ${OLDPWD} } # ${OHOS_SDK} oh sdk安装路径# $ARCH 编译的架构是 archs 的遍历# $LYCIUM_ROOT /usr/$pkgname /$ARCH 安装到顶层目录的usr/$pkgname /$ARCH # 执行编译构建的命令 build() { cd $builddir/$ARCH-build ../configure $* --build=x86_64-unknown-linux-gnu --host=$myhost --with-sysroot=/${OHOS_SDK}/native/sysroot --disable-winstore-app --disable-dbus --disable-debug --disable-gprof --disable-cprof --disable-coverage --enable-optimizations --disable-mmx --disable-sse --disable-neon --disable-arm64 --disable-altivec --disable-lua --disable-vlm --disable-addonmanagermodules --disable-archive --disable-live555 --disable-dc1394 --disable-dv1394 --disable-linsys --disable-dvdread --disable-dvdnav --disable-bluray --disable-opencv --disable-smbclient --disable-dsm --disable-sftp --disable-nfs --disable-smb2 --disable-v4l2 --disable-decklink --disable-vcd --disable-libcddb --disable-screen --disable-vnc --disable-freerdp --disable-realrtsp --disable-macosx-avfoundation --disable-asdcp --disable-dvbpsi --disable-gme --disable-sid --disable-ogg --disable-shout --disable-matroska --disable-mod --disable-mpc --disable-wma-fixed --disable-shine --disable-omxil --disable-omxil-vout --disable-rpi-omxil --disable-crystalhd --disable-mad --disable-mpg123 --disable-gst-decode --disable-merge-ffmpeg --disable-avcodec --disable-libva --disable-dxva2 --disable-d3d11va --disable-avformat --disable-swscale --disable-postproc --disable-ffad --disable-aom --disable-dav1d --disable-vpx --disable-twolame --disable-fdkaac --disable-a52 --disable-dca --disable-flac --disable-libmpeg2 --disable-vorbis --disable-tremor --disable-speex --disable-opus --disable-spatialaudio --disable-theroa --disable-oggspots --disable-daala --disable-schroedinger --disable-png --disable-jpeg --disable-bpg --disable-x262 --disable-x265 --disable-x264 --disable-x26410b --disable-mfx --disable-fluidsynth --disable-fluidlite --disable-zvbi --disable-telx --disable-libass --disable-aribsub --disable-aribb25 --disable-kate --disable-tiger --disable-css --enable-gles2 --disable-xcb --disable-xvideo --disable-vdpau --disable-wayland --disable-sdl-image --disable-freetype --disable-fribidi --disable-harfbuzz --disable-fontconfig --disable-svg --disable-svgdec --disable-directx --disable-aa --disable-caca --disable-kva --disable-mmal --disable-evas --disable-pulse --disable-alsa --disable-oss --disable-sndio --disable-wasapi --disable-jack --disable-opensles --disable-tizen-audio --disable-samplerate --disable-soxr --disable-kai --disable-chromaprint --disable-chromecast --disable-qt --disable-skins2 --disable-sparkle --disable-ncurses --disable-lirc --disable-srt --disable-goom --disable-projectm --disable-vsxu --disable-avahi --disable-udev --disable-mtp --disable-upnp --disable-microdns --disable-libxml2 --disable-libgcrypt --disable-gnutls --disable-taglib --disable-secret --disable-kwallet --disable-update-chcek --disable-osx-notifications --disable-notify --disable-libplacebo --enable-run-as-root make -j4 ret=$? cd $OLDPWD return $ret } # 打包安装 package() { cd $builddir make -C $ARCH-build install cd $OLDPWD } # 进行测试的准备和说明 check() { echo "The test must be on an OpenHarmony device!" } # 清理环境 cleanbuild() { rm -rf ${PWD}/$builddir #${PWD}/$packagename }
适配 可能这时你会有疑问,为什么 configure
的时候这么多 disable
呢,因为鸿蒙没有这些三方库(或许是部分,可惜 configure
的时候一直在找宿主机的三方库),SDK里面找不到他们。
好了,让我们进入错综复杂的 make
环节
pthread 起手 请注意:我没能解决这个问题
1 2 3 error: call to undeclared function 'pthread_cancel'; ISO C99 and later do not support implicit function declarations [-Werror,-Wimplicit-function-declaration] error: call to undeclared function 'pthread_setcancelstate'; ISO C99 and later do not support implicit function declarations [-Werror,-Wimplicit-function-declaration] error: call to undeclared function 'pthread_testcancel'; ISO C99 and later do not support implicit function declarations [-Werror,-Wimplicit-function-declaration]
和安卓类似,鸿蒙针对 pthread
做了许多限制,如上三个函数都不能使用
你可以很轻松的在网上找到 pthread_cancel
pthread_setcancelstate
的 解决方案
但是 pthread_testcancel
始终找不到
当然,你也可以试试这个 libbthread
尽管我在尝试的时候有一些未记录的bug,比如 pthread.h
缺少宏定义之类的,但是说不定有大用
由于时间有限,我选择了最坏的解决方式 ,GPT随便生成了一个塞进去了,能通过编译。
我把下面这段塞进了 vlc/src/posix/thread.c
里面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 #define SIG_CANCEL_SIGNAL SIGUSR1 static inline int pthread_cancel (pthread_t thread) { return pthread_kill(thread, SIG_CANCEL_SIGNAL); } static int pthread_setcancelstate (int state, int *oldstate) { sigset_t new, old; int ret; sigemptyset (&new); sigaddset (&new, SIG_CANCEL_SIGNAL); ret = pthread_sigmask(state == PTHREAD_CANCEL_ENABLE ? SIG_BLOCK : SIG_UNBLOCK, &new , &old); if (oldstate != NULL ) { *oldstate =sigismember(&old,SIG_CANCEL_SIGNAL) == 0 ? PTHREAD_CANCEL_DISABLE : PTHREAD_CANCEL_ENABLE; } return ret; } void pthread_testcancel (void ) { sigset_t pending; sigemptyset(&pending); if (sigpending(&pending) == 0 && sigismember(&pending, SIG_CANCEL_SIGNAL)) { pthread_exit(PTHREAD_CANCELED); } }
posix 的重重困扰 1 error: call to undeclared function 'posix_close'; ISO C99 and later do not support implicit function declarations [-Werror,-Wimplicit-function-declaration]
这种鸿蒙里面没有 posix 的函数问题可太多了,我难以全部解决。
决定 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 SUBDIRS = compat src lib libcompat: cd compat && $(MAKE) $(AM_MAKEFLAGS) libvlccore: libcompat cd src && $(MAKE) $(AM_MAKEFLAGS) libvlccore.la libvlc: libvlccore cd lib && $(MAKE) $(AM_MAKEFLAGS) libvlc.la .PHONY : libvlc
这就是我的终极解决方案
部署 经验证,这样确实可以编译通过并生成 libvlc.so
和 libvlccore.so
放到板子上也能够成功使用 version
函数
可惜的是,调用 libvlc_new
返回值始终是 NULL
官方文档解释,需要为 src
设置环境变量;网上解释,需要把 modules
放到可执行文件的同文件夹下。
由于我打包成了 HPK
到开发板上运行, 这两种方法都难以解决我的问题。
思考
由于时间和设备限制,我没有尝试过使用 .a
文件(主要是忘了),感觉应该有戏
其实应该重点改造 vlc-for-android
,但是 SDK
差距太大,项目也比较复杂,本人能力有限