最新在Bring up公司的播放器从Android 4.4到Android Q,期间遇到很多“坑”,总结出来记录一下。期间也会简单介绍一下在Android系统中新建一个类似Nuplayer的播放器大概需要哪些步骤。

代码目录:frameworks/av/media/libmediaplayerservice/

最先动的地方是MediaPlayerFactory.cpp,我的做法是根据项目需求,选择是否走自己的播放器还是Android原生播放器(因为过XTS认证会走Android原生flow),改动如下(记得把RAIN_PLAYER的define放在跟NuPlayer一样的地方,以后这样简单的事情就不在赘述啦^_^):

void MediaPlayerFactory::registerBuiltinFactories() {Mutex::Autolock lock_(&sLock);if (sInitComplete)return;IFactory* factory = new NuPlayerFactory();if (registerFactory_l(factory, NU_PLAYER) != OK)delete factory;factory = new TestPlayerFactory();if (registerFactory_l(factory, TEST_PLAYER) != OK)delete factory;#ifdef BUILD_WITH_XXXX_MMregisterFactory_l(new RainPlayerFactory(), RAIN_PLAYER);
#endifsInitComplete = true;
}

分享一个小技巧,我们公司的coding style是如果有动到Android原生的部分,要在改动上下加上如下打印:

// xxxx Android Patch Begin

// xxxx Android Patch End

RainPlayerFactory部分如下,至于自己的player放在哪里,完全由自己决定,可以选择放在frameworks/av/media/libmedia或是其他什么地方,我放在了vendor目录。

// xxxx Android Patch Begin
class RainPlayerFactory : public MediaPlayerFactory::IFactory {public:virtual float scoreFactory(const sp<IMediaPlayer>& /* client */,const char* url,const sp<IMediaHTTPService>& /* httpService */,float curScore) {static const float kOurScore = 2.0;   // 定义一个比较高的分数,PK赢了会走自己的playerif (kOurScore <= curScore)return 0.0;if (!strncasecmp("http://", url, 7)|| !strncasecmp("https://", url, 8)) {size_t len = strlen(url);if (len >= 5 && !strcasecmp(".m3u8", &url[len - 5])) {
#ifdef SPECIAL_STREAM_USE_NUPLAYERif (strstr(url, "/sample_aes/")) {return 0.0;}
#endifreturn kOurScore;}if (strstr(url,"m3u8")) {
#ifdef SPECIAL_STREAM_USE_NUPLAYERif (strstr(url, "/sample_aes/")) {return 0.0;}
#endifreturn kOurScore;}}if (!strncasecmp("rtsp://", url, 7)) {return kOurScore;}return 0.0;}virtual float scoreFactory(const sp<IMediaPlayer>& /* client */,const sp<IStreamSource>& /* source */,float /* curScore */) {return 1.0;}virtual sp<MediaPlayerBase> createPlayer(pid_t /* pid */) {ALOGV("Create RainPlyer");return new RainPlyer;}
};#ifdef SUPPORT_RAINPLAYER
class RainPlayerFactory : public MediaPlayerFactory::IFactory {public:virtual sp<MediaPlayerBase> createPlayer(pid_t /* pid */) {ALOGV("Create RainPlyer");return new RainPlyer;}
};
#endif
// xxxx Android Patch End

编译部分,我在mediaplayerservice加入了soong,这样可以根据BoardConfig.mk来进行动态编译,简单来说就是编译中的环境变量会传给libmediaplayerservice.go比如可以加变量来动态选择走NuPlayer还是RainPlayer,只在BoardConfig.mk中改一行配置信息即可。BoardConfig.mk修改之后,编译系统会自动去检测:

device/xxx/xxx/BoardConfig.mk was modified, regenerating...

修改frameworks/av/media/libmediaplayerservice/Android.bp:

    // xxx Android Patch Begindefaults: ["mediaplayerservicedefaults",],// xxx Android Patch End

添加frameworks/av/media/libmediaplayerservice/soong/Android.bp:

bootstrap_go_package {name: "soong-mediaplayerservice",pkgPath: "android/soong/vendor/xxx/mediaplayerservice",deps: ["blueprint","blueprint-pathtools","soong","soong-android","soong-cc",],srcs: ["libmediaplayerservice.go",],pluginFor: ["soong_build"],
}mediaplayerservicedefaults {name: "mediaplayerservicedefaults",
}

添加frameworks/av/media/libmediaplayerservice/soong/libmediaplayerservice.go:

package libmediaplayerserviceimport ("fmt""android/soong/android""android/soong/cc"
)//Build Option
const BUILD_WITH_RIAN    string = "BUILD_WITH_RIAN"func init() {android.RegisterModuleType("mediaplayerservicedefaults", mediaplayerserviceDefaultsFactory)
}func mediaplayerserviceDefaultsFactory() android.Module {module := cc.DefaultsFactory()android.AddLoadHook(module, globalMediaPlayerServiceDefault)return module
}func globalMediaPlayerServiceDefault(ctx android.LoadHookContext) {type props struct {Srcs []stringInclude_dirs []stringShared_libs []stringStatic_libs []stringCflags []string}p := &props{}p.Srcs         = globalMediaPlayerServiceSrcs(ctx)p.Include_dirs = globalMediaPlayerServiceIncludeDirs(ctx)p.Shared_libs = globalMediaPlayerServiceSharedLibs(ctx)p.Static_libs = globalMediaPlayerServiceStaticLibs(ctx)p.Cflags       = globalMediaPlayerServiceCflags(ctx)ctx.AppendProperties(p)
}func globalMediaPlayerServiceSrcs(ctx android.BaseContext) ([]string) {var srcs []stringreturn srcs
}func globalMediaPlayerServiceIncludeDirs(ctx android.BaseContext) ([]string) {var includeDirs []stringvar tmp stringif (envIsTrue(ctx, BUILD_WITH_RIAN) {// 在这里可以加入你新建Player的相关头文件includeDirs = append(includeDirs, "external/skia/include")includeDirs = append(includeDirs, "external/skia/include/core")includeDirs = append(includeDirs, "vendor/XXX/common/libraries/media/mmplayer/")}return includeDirs
}func globalMediaPlayerServiceSharedLibs(ctx android.BaseContext) ([]string) {var shared_libs []stringif (envIsTrue(ctx, BUILD_WITH_RIAN) {// 在这里可以加入你新建Player的相关动态链接库shared_libs = append(shared_libs, "librainplayer")}return shared_libs
}func globalMediaPlayerServiceStaticLibs(ctx android.BaseContext) ([]string) {var static_libs []stringif envIsTrue(ctx, BUILD_WITH_MARLIN) {// 在这里可以加入你新建Player的相关静态链接库static_libs = append(static_libs, "libstagefright_nuplayer_wasabi")static_libs = append(static_libs, "libWasabi")}return static_libs
}func globalMediaPlayerServiceCflags(ctx android.BaseContext) ([]string) {var cflags []string// 如果没成功,可以在这里加打印,看看env有没有设置下来fmt.Println("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")fmt.Println("BUILD_WITH_RIAN:",ctx.AConfig().IsEnvTrue("BUILD_WITH_RIAN"))if (envIsTrue(ctx, BUILD_WITH_RAIN) {// 在这里可以加入你新建Player的条件编译// 与Android.mk的LOCAL_CFLAGS += -DBUILD_WITH_RAIN类似cflags = append(cflags, "-BUILD_WITH_RAIN")}return cflags
}func envDefault(ctx android.BaseContext, key string, defaultValue string) string {ret := ctx.AConfig().Getenv(key)if ret == "" {return defaultValue}return ret
}func envIsOptionValue(ctx android.BaseContext, key string, value string) bool {return ctx.AConfig().Getenv(key) == value
}func envIsNotOptionValue(ctx android.BaseContext, key string, value string) bool {return ctx.AConfig().Getenv(key) != value
}func envIsTrue(ctx android.BaseContext, key string) bool { //ctx android.LoadHookContext) {return ctx.AConfig().IsEnvTrue(key);
}func printLog(msg string) {fmt.Println(msg)
}func print2Log(msg string, param string) {if param != "" {fmt.Errorf(msg + ", %s", param)}
}

mediaplayerservice的工作量基本就这些,剩下再往下就是播放器本身的部分,还有OpenMax、display...这些部分涉及公司的代码太多,不好粘贴了。接下说一下遇到的坑...

坑1:

如果编译的模块开始vndk,你又修改了他的API,那么VNDK就会报错,出错信息一般长这样:

******************************************************

error: VNDK library: libmedia_omx's ABI has EXTENDING CHANGES Please check compatiblity report at : out/soong/.intermediates/frameworks/av/media/libmedia/libmedia_omx/android_arm_armv8-a_cortex-a53_vendor_shared/libmedia_omx.so.abidiff

******************************************************

---- Please update abi references by running platform/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l libmedia_omx ----

我总结了如下几种方式:

1、最粗暴的(不推荐)

在BoardConfig.mk中加入如下两行,可以在编译的时候不去check VNDK,关掉这个可能会导致XTS某些测项跑不过,一般code review也不会给过...

BOARD_VNDK_VERSION :=
BOARD_VNDK_RUNTIME_DISABLE := true

2、一般粗暴:

删除如下目录的这些文件

/prebuilts/abi-dumps/vndk

./28/32/arm_armv7-a-neon/source-based/libion.so.lsdump.gz

./28/32/x86/source-based/libion.so.lsdump.gz

./28/64/arm64_armv8-a/source-based/libion.so.lsdump.gz

./28/64/arm_armv8-a/source-based/libion.so.lsdump.gz

./28/64/x86/source-based/libion.so.lsdump.gz

./28/64/x86_64/source-based/libion.so.lsdump.gz

./28/64/x86_x86_64/source-based/libion.so.lsdump.gz

我最终在本地的解法就是进入到/prebuilts/abi-dumps/vndk,搜索libmedia_omx, 将搜索到的libmedia_omx.so.lsdump*全部删除。并重新编译,结果就build pass了。这样也不太好,如果你上代码到服务器,自己这样一搞可以编过,那别人就惨了...

3、正解(推荐):

其实报错信息有让你去跑一个脚本,之前我也有试过,始终跑不过,找了很多资料,结果少了-products xxx // xxx为boardname,敲如下命令就build pass了,上code到服务器,完美~

development/vndk/tools/header-checker/utils/create_reference_dumps.py -l libmedia_omx -products xxx // xxx为boardname

坑2:

error: frameworks/av/media/libmediaplayerservice/Android.bp:1:1: dependency "librain" of "libmediaplayerservice" missing variant:
  arch:android_arm64_armv8-a_cortex-a53, image:core, link:shared, version:
available variants:
  arch:android_arm_armv7-a-neon_cortex-a7, image:core, link:shared, version:
FAILED: [W][2019-10-21T15:15:03+0800][1] bool caps::initNs(nsjconf_t *)():215 prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL): Invalid argument
15:15:21 soong bootstrap failed with: exit status 1
初步看起来像是想要去编译librain的64位的其他架构的版本,晚上也有很多种情况,这里我遇到的情况是,librain的Android.bp中动态链接了一些vendor的底层lib,导致libmediaplayerservice链接librain的时候报错,拿掉这些lib之后就build pass了。

shared_libs: ["libc","liblog","libdl","libcutils","libutils","libui","libmedia","libsurfaceflinger","libbinder","libstagefright","libstagefright_foundation","libz","libcrypto","libgui",//"android.hidl.allocator@1.0",//"android.hidl.memory@1.0",//"android.hardware.media.omx@1.0",
],

坑3:

FAILED:
build/make/core/base_rules.mk:325: error: vendor/xxx/common/libraries/media/mmplayer/mmp: MODULE.TARGET.SHARED_LIBRARIES.libmmp_32 already defined by vendor/xxx/common/libraries/media/mmplayer/mmp.
15:02:20 ckati failed with: exit status 1

#### failed to build some targets (02:35 (mm:ss)) ####
因为是Andorid 4.4移植到Android 10.0,所以原来Android 4.4的Android.mk还存在各个目录,如果Android.mk和Android.bp定义了相同的moudle就会报如上错误。解决方式也很简单,我的方法是在Android.mk中加入如下条件,若是API 28以下让整个Andorid.mk失效,这样既不会影响老版本的Andorid项目,也解决的Android Q的编译问题。

ifeq (1, $(strip $(shell expr $(PLATFORM_SDK_VERSION) \< 28)))
xxx
endif

坑4:

FAILED: out/soong/.intermediates/vendor/xxx/common/libraries/media/mmplayer/mmp/libmmp/android_arm_armv7-a-neon_cortex-a7_core_shared/libmmp.so.toc
echo "module libmmp missing dependencies: libmmdisp" && false
module libmmp missing dependencies: libmmdisp
[ 97% 987/1013] //vendor/xxx/common/libraries/media/mmplayer/mmp:libmmp strip libmmp.so [arm]
FAILED: out/soong/.intermediates/vendor/xxx/common/libraries/media/mmplayer/mmp/libmmp/android_arm_armv7-a-neon_cortex-a7_core_shared/libmmp.so
echo "module libmmp missing dependencies: libmmdisp" && false
module libmmp missing dependencies: libmmdisp
[ 97% 988/1013] //vendor/xxx/common/libraries/media/mmplayer/mmp:libmmp link libmmp.so [arm]
FAILED: out/soong/.intermediates/vendor/xxx/common/libraries/media/mmplayer/mmp/libmmp/android_arm_armv7-a-neon_cortex-a7_core_shared/unstripped/libmmp.so
echo "module libmmp missing dependencies: libmmdisp" && false
module libmmp missing dependencies: libmmdisp

libmmdisp是我司的显示模块,跟我移植的播放器不是同一process,所以要动态链接一下,链接libmmdisp时就会报错,然后本身libmmdisp编译是可以pass的。

原因是libmmdisp还是延用之前Android.mk的编译方式,我的player是用Android.bp去链接libmmdisp,所以找不到。解法自然是在libmmdisp中引入Android.bp的编译方式。

编译上比较棘手的坑大概就这些了,这种工作势必会经历如下阶段:

代码编不过;

代码编过了链接出问题;

链接没问题了,编版本烧录起不来;

平台起来了,播放会挂掉;

播放不挂了,但是黑屏,显示不正常;

最后显示正常,剩一些无声,硬解码起不来等小bug在慢慢修...

如上每一步都遇到了,要时刻保持良好心态,一步一个脚印,不多说了,解bug去了,o(╥﹏╥)o...

Android Q播放器(编译报错处理)相关推荐

  1. Android 12源码编译报错:FAILED: out/soong/build.ninja

    Android 12源码编译报错:FAILED: out/soong/build.ninja android12源码编译中报如下错误: 网上查了比较多资料发现是swap分区不够导致的,报错时使用fre ...

  2. android icu4c 7.1编译报错,android4.0编译系统时候遇到的错误集

    1.Android library 'XXX.so' not in prelink map 错误解决 在编译自己的Android library时可能会出现 library 'XXX.so' not ...

  3. android icu4c 7.1编译报错,icu4c中资源的修改,编译

    最近在修改android中汉字的多音字问题(曾,ceng zeng),遇到修改icu4c/data/coll/zh.txt文件,但是make -j4没有效果. "关于icu4c库里的资源这里 ...

  4. 【错误记录】Android Studio 编译报错 ( SDK location not found )

    文章目录 一.报错信息 二.解决方案 一.报错信息 Android Studio 编译报错 : 首次打开别人的项目 , 经常性的报错 ; Could not determine the depende ...

  5. Android 编译报错:Could not get resource

    编译报错: Could not resolve all dependencies for configuration ':app:debugRuntimeClasspath'. Could not d ...

  6. AS升级3.1 编译报错:The SourceSet 'instrumentTest' is not recognized by the Android Gradle Plugin.

    AndroidStudio升级到3.1后编译报错:The SourceSet 'instrumentTest' is not recognized by the Android Gradle Plug ...

  7. AS升级编译报错:The SourceSet 'instrumentTest' is not recognized by the Android Gradle Plugin....

    AS升级3.2.0 编译报错:The SourceSet 'instrumentTest' is not recognized by the Android Gradle Plugin. 解决方法:将 ...

  8. 【错误记录】Android Studio 编译报错 ( Could not determine java version from ‘11.0.8‘. ② | 升级 Gradle 版本 )

    文章目录 一.报错信息 二.解决方案 解决 Could not determine java version from '11.0.8'. 问题 , 有两种方案 , 方案一 : Android Stu ...

  9. 【错误记录】Android Studio 编译报错 ( A problem occurred starting process ‘command ‘ninja.exe‘ ‘ )

    文章目录 一.报错信息 二.解决方案 一.报错信息 Android Studio 编译报错 : FAILURE: Build failed with an exception.* What went ...

  10. 【错误记录】Android Studio 4.2.1 编译报错 ( 设置支持的 Java 和 Kotlin 版本 | java.lang.BootstrapMethodError )

    文章目录 一.报错信息 二.解决方案 一.报错信息 更新了 Android Studio 到 4.2.14.2.14.2.1 , 打开之前的老项目 , 部分界面是由 Kotlin 开发的 , 进入 K ...

最新文章

  1. Mysql 事务中Update 会锁表吗?
  2. 澳大利亚悉尼大学徐畅教授招收深度学习方向全奖博士生
  3. c#中ref与out的区别
  4. typeScript面试必备之-通识七:typeScript中的可索引接口(数组,对象)+类类型接口...
  5. 强化学习(六)时序差分在线控制算法SARSA
  6. Linux中增加软路由的两种方法,Linux中增加软路由的三种方法
  7. 天翼云从业认证(3.1)了解天翼云计算产品
  8. 【.Net 学习系列】-- EF Core实践(Code First)
  9. 台湾大学林轩田机器学习基石课程学习笔记5 -- Training versus Testing
  10. 什么是js的严格模式
  11. ASP.NET MVC 使用Log4Net在不同目录中记录不同类型的日志
  12. wpf 怎样判断是否选中 checkbox_怎样判断自己的肌肤是否缺水?
  13. 机器学习ai选股_自带AI机器学习的MEMS了解一下
  14. [知识整理]Linux系统WIFI知识的一些整理
  15. 【语音隐写】基于matlab GUI DWT+SVD音频水印嵌入【含Matlab源码 1409期】
  16. 关于GPS坐标系和地图定位偏差
  17. 小强学渲染之OpenGL的GPU管线
  18. 银行手机APP安全评估报告【转载】
  19. Nacos 修改密码
  20. 前后端分离 Spring Security 对登出.logout()的处理

热门文章

  1. beamer插入图片_用Latex制作幻灯片(beamer)
  2. 如何保存在线文档html代码,html代码怎样保存和使用
  3. 计算机网络——域名系统
  4. 三体归零者和盘龙鸿蒙,《三体》中归零者这样的大神级文明已经脱离黑暗森林和猜疑链了吗,为什么?...
  5. 你不得不知的网络编程三剑客
  6. 大厂HR面试会问什么?
  7. 第五节:蜂鸣器的驱动程序
  8. 计算机程序设计专业大学排名,全国计算机专业大学排名一览表
  9. MySQL按日期依次统计
  10. 计算机考研复试-英文问答