猫耳 Android 播放框架开发实践
概述
猫耳FM是中国最大的 95 后声音内容分享平台,是B站重要平台之一,深度合作国内顶级声优工作室,打造了数百部精品广播剧,全站播放总量超过百亿次。

MEPlayer 是猫耳 Android 技术团队研发的一款适用于音视频、直播、特效播放等多种场景的跨进程播放框架。目前支持:
- 音视频、直播、特效播放。
- 支持自定义播放内核,目前内置了 exo、bbp(多媒体部门开发的轻量级播放内核),都添加了边下边播支持,可以自行扩展支持 ijk 等内核,实现固定的几个接口即可。
- 与业务完全解耦,已在公司内多个团队使用。
- 跨进程播放音视频、直播、特效,播放进程被杀后自动恢复。
- 自动管理音频焦点,支持忽略焦点抢占(与其他音视频应用同时播放)。
- 支持显示在通知栏和播控中心,已适配包括鸿蒙(猫耳FM 已通过华为测试,配置在鸿蒙系统源码白名单里)在内的国内系统。
- 后台播放优化:bbp 后台播视频支持暂停视频解码;播放时后台保持网络连接;能同时提升主进程和播放进程优先级保证应用播放时存活更久。
- 支持切换清晰度、播放中途音视频互切、起播出错或者播放中途出错自动重试。
- 基础功能:播放列表、循环模式、进度跳转、快进、快退、倍速播放、设置音量、跳过片头片尾、定时暂停、播放完整首暂停等。
具体使用场景可以参考猫耳FM APP:
音视频主播放场景: 音视频播放页;
直播、特效: 直播间;
短视频场景: 首页推荐 -> 小梦乡;
列表播放以及过渡到播放页场景: 首页推荐 -> 播放大卡;
配音秀: 发现 -> 活动 -> 配音活动;
单个音视频、特效播放场景: 个人主页头像音、首页点击盲盒剧场、活动 -> 运势语音、首页声音恋人 tab 下的推荐 UP 主播放、我的 -> 启动音等。
起源
旧版本猫耳FM APP 内的大量的音视频播放场景,使用了 ijk、ExoPlayer、MediaPlayer 等多种播放器方案,且播放逻辑和业务逻辑高度耦合,当播放场景出现新的需求,改动成本巨大,且编写需求代码的过程中易产生 bug;原播放场景相关的代码缺少模块化,代码复用程度低,进而影响后期维护。因此,项目迫切需要一套统一的播放框架,以满足不同场景的需要。调研了主流的播放框架之后,发现很难同时满足我们的多样化场景。在调研了主流的播放框架后,发现没有现成方案能够满足项目的多样化场景,于是我们开发了 MEPlayer,0 重复逻辑、0 业务耦合,API 友好,开发的理解和接入成本都极小。
播放器流程
下图是一个简单的播放流程图。

MEPlayer、MEDirectPlayer 是音视频和直播业务直接接触的两个播放器入口,MEPlayer 支持跨进程播放,MEDirectPlayer 则直接在主进程播放,这两个 Player 的基础 API 和播放逻辑代码都是共享的,差异部分在于播放器入口实例和内核封装 Player 的连接,相比于 MEPlayer,MEDirectPlayer 缺少连接播控中心的能力。
为什么需要 MEDirectPlayer 呢?因为对于闪屏、启动音等在启动 APP 一两秒内就要播放的场景,跨进程播放是来不及的,可能会出现需要播的时候进程还没连接好的状况。而跨进程部分逻辑是比较复杂的,所以还是分离一个播放器入口对于后期维护和业务理解都更友好。
对于视频和特效播放,需要绑定视频/特效容器的 Surface,SurfaceListener 是在播放器内部管理的,业务只需要传递容器 View 给播放框架即可,目前支持 TextureView 和 SurfaceView,业务如果设置过 SurfaceListener,框架里也会兼容,在对应方法回调时,会给老的 listener 同时回调,在列表场景视频卡片切换时,会把业务设置的 listener 还给上一个卡片。特效播放比较特殊,播放器入口是 AlphaVideoPlayer,用到的播放内核 API 也不一样,在跨进程 AIDL 调用中都是独立的方法,但是业务调用的 API 跟音视频播放是一致的。
播放框架的状态机见下图:

起播处理流程采用的拦截器模式,对于全局的 https、免流处理等操作,可以自定义一个拦截器注入到播放器中,对于列表播放中某一条 item 没有 url 信息时,也可以在默认的拦截器回调中请求接口返回一个新的 url 来播放。
interface PlayerPreProcessor {val name: String/*** Processor id,业务自定义的 id 从 100 开始定,前 100 是给框架预留的*/val id: Int/*** 处理器调用优先级,值越大优先级越大,最大为 100。设置的时候注意查看现有的其他处理器的优先级,尽量不要重复*/@get:IntRange(from = 0L, to = 100L)val priority: Int/*** @param url 原始 url* @param playItem 播放列表中的当前 item,如果没有列表则为空* @param playParam 播放参数* @param scope 协程作用域* @return 输出的结果*/suspend fun process(url: String?, playItem: PlayItem?, playParam: PlayParam?, scope: CoroutineScope): PlayerPreProcessResult
}

播放器回调统一采用 kotlin dsl 的形式,简单示例如下:
private val mPlayer = MEPlayer(this).apply {onReady {// 打开 url 资源成功回调}onDuration {// 更新时长}onPlayingStateChanged { isPlaying, from ->// 更新播放状态}onPositionUpdate {// 更新播放进度}onCompletion { // 播放结束}onRetry {// 播放出错会自动调用 onRetry 进行重试,如果业务没有实现则跳转到 onError// onRetry 是一个 suspend 方法,可以进行耗时操作,需要返回一个 url,可以是 player.originUrl,也可以是请求后端返回的一个新 url}onError {// 错误处理}
}
MEPlayer 支持传入 LifecycleOwner,可以在 LifecycleOwner onDestroy 的时候自动释放。构造方法为:
/*** 播放器构造方法,大多数场景都应该使用 MEPlayer,会跨进程播放** @param lifecycleOwner LifecycleOwner 对象,对于可以在退出页面后继续播放的场景,可以传 ProcessLifecycleOwner.get(),其他场景可以传页面的 LifecycleOwner* @param from 用于在日志 tag 上显示业务来源,可以传页面的 TAG,默认使用 lifecycleOwner 所在页面的 className* @param type 播放器类型,默认值为 PLAYER_TYPE_AUTO* PLAYER_TYPE_AUTO -> 根据磁盘缓存键值对里 “player_type” 对应的值来选择播放器,如果是 “exo” 则使用 ExoPlayer,* 如果是 “bbp” 则使用 BBP 播放器,默认使用 ExoPlayer。* PLAYER_TYPE_BB_PLAYER -> 使用 BBP 播放器* PLAYER_TYPE_EXO_PLAYER -> 使用 ExoPlayer* @param scope 协程作用域,用于播放器对象里创建协程,管理协程生命周期,默认值为 lifecycleOwner.lifecycleScope*/
class MEPlayer @JvmOverloads constructor(lifecycleOwner: LifecycleOwner,from: String = lifecycleOwner.tagName(),@PlayerType type: String = PLAYER_TYPE_AUTO,scope: CoroutineScope = lifecycleOwner.lifecycleScope
)
播放框架还支持多实例场景,配音秀和小梦乡场景都是无声视频配合音频一起播放的,所以跨进程播放的时候要支持多个实例同时播放。先看下播放器的一段日志:
// 音频
// 播放进程
I/ServicePlayer.Hypnosis.bbp.core1 onReady
I/ServicePlayer.Hypnosis.bbp.core1 onPlaying, needRequestFocus: true
I/ServicePlayer.Hypnosis.bbp.core1 updatePlaybackState, shouldShowInMediaSession: true, enableNotification: true, enableRating: false, enableLyric: false
// 主进程
I/MEPlayer.Hypnosis.bbp.core1 onReady
I/MEPlayer.Hypnosis.bbp.core1 updatePlayingState, isPlaying: true, reason: 1 (open), position: 12 (00:00), notifyCallback: true, notifyNotification: true
// 视频
// 播放进程
I/ServicePlayer.HypnosisHomeFragment.bbp.core2 onReady
I/ServicePlayer.HypnosisHomeFragment.bbp.core2 onPlaying, needRequestFocus: false
I/ServicePlayer.HypnosisHomeFragment.bbp.core2 updatePlaybackState, shouldShowInMediaSession: false, enableNotification: false, enableRating: false, enableLyric: false
// 主进程
I/MEPlayer.HypnosisHomeFragment.bbp.core2 onReady
I/MEPlayer.HypnosisHomeFragment.bbp.core2 updatePlayingState, isPlaying: true, reason: 1 (open), position: 21 (00:00), notifyCallback: true,
可以看出,播放器日志采用了多级 TAG 结构,在播放框架的主流程的每一个类中,打印的日志都能直接看出当前打印日志时所在的类、业务、播放内核类型和内核实例索引。播放器实例采用 SparseArrayCompat 来存储,主进程和播放进程保证实例索引的一一对应关系。
在列表视频播放过渡到播放页场景中,需要做到实例无缝过渡,框架里会把播放页实例的参数传递给列表的实例,然后释放原实例,整个过程播放是持续进行的。
播放器优化
在网络连接上,ExoPlayer 官方已经支持了 Cronet,经过和多媒体部门、主站一起合作,bbp 也添加了 Cronet 支持,Cronet 是一个由 Google 开发的网络库, 也是 Chrome 的网络栈,它提供了高性能和可靠的网络访问能力,支持 HTTP、HTTP/2 以及 HTTP/3 协议,在 HTTP/3 下,90% 的用户起播速度提升了 100ms 以上。

另外 ExoPlayer 的缓存支持其实并不友好,音频 APP 的一个必备功能就是在播放的时候会持续缓存完整个音频,同时进度条会更新缓存进度,但是要想用 ExoPlayer 直接实现这点,很难,业内一般是用 AndroidVideoCache 来实现的,并不优雅,这里我修改了部分 ExoPlayer 的源码,添加了支持,内容较长不好展开讲。
音频焦点管理
音频焦点在框架内自动申请和释放,业务只需要在初始化播放器时设置音频焦点类型和是否忽略焦点抢占(即和其他应用同时播放)即可。
player.run {audioFocusGain = AUDIO_FOCUS_GAIN_TRANSIENTignoreFocusLoss = true
}
在每个播放器实例中都会有焦点监听和处理
后台播放优化

在应用退到后台后,如果进程(包括主进程)不是前台进程,很可能会在几秒内被系统杀死。那么就需要在播放的时候通过调用 startForeground(int id, Notification notification) 将播放进程设置为前台进程,前台进程需要绑定一个通知,退到后台后,可以发现播放进程的存活率明显提升,但是播一会儿你会发现,主进程没了。就是说主进程和播放进程都需要设置为前台进程,但是产品需求上我们只有一个播放器通知,所以主进程要用和播放进程一样的通知内容开启前台进程,以保证用户切换音频的时候不会看到闪出一个非播放通知。这里我们主进程也开了个通知服务来更新通知,播放进程只需要开启前台进程的时候绑定通知就好了,后续通知的更新交由主进程完成。播放时退后台打印优先级可以看到两个进程都是较高的优先级。
> adb shell
$ cat /proc/`pidof cn.missevan`/oom_adj
3
$ cat /proc/`pidof cn.missevan:player`/oom_adj
3
还有一种情况是,主进程活着,但是播放进程被杀死了,或者播放进程出现问题崩溃了,这时候主进程需要恢复播放进程,不仅仅是启动进程,也需要维持原有的进度恢复播放,还需要创建新的通知开启前台进程。这些步骤都需要拿到原有的数据,在播放进程存放这些数据不靠谱,所以主进程执行的步骤,都需要保存数据,以供播放进程重连后使用。

播放失败重试包含中途网络断开媒体数据却没有缓存完、链接失效、seek 失败、切换清晰度失败、音视频切换失败等场景,这些场景的重试逻辑是有所区分的,要保证代码逻辑清晰符合需求又没有重复代码是比较困难的,好在梳理异同点后把逻辑都聚合到了一块,对于后期扩展也比较友好。这里通过 playType 区分场景,核心逻辑如下:
val playParamApplier: PlayParam.() -> Unit = {// 重试的时候复用上次的参数from(currentPlayParam)// 重试都是保持原来设置的 playWhenReady,即使原始请求是不要 keepPlayingState 的,重试也可以设为 true,因为原始请求已经生效了,重试就可以保持了keepPlayingState = trueisSwitchUrl = truestopPrevious = falseisRetry = true// 针对有的错误,转换播放类型when (errorCode) {PLAYER_ERROR_CODE_OPEN_FAILED -> {// 打开失败的情况直接按原来的参数重新打开即可,isSwitchUrl 要传 false,否则会没有 onReady、onDuration 回调isSwitchUrl = falseposition = this@BaseMediaPlayer.position}PLAYER_ERROR_CODE_SEEK_FAILED -> {playType = PLAYER_PLAY_TYPE_SEEK_RETRY}PLAYER_ERROR_CODE_SWITCH_QUALITY_FAILED -> {// bbp 切换清晰度第一次出错以后会走到这里执行重试,重试需要换播放类型playType = PLAYER_PLAY_TYPE_SWITCH_QUALITY_RETRY}}
}
进入后台和离开视频页后暂停视频解码,需要设置对应视频容器所在页面的 LifecycleOwner,调用 videoPageLifecycleOwner = this@XXXFragment 即可,如果没有设置则会使用构造方法里的 LifecycleOwner。在后台播放时使用 WifiLockManager 和 WakeLockManager 启用 Wi-Fi 锁和唤醒锁可以让应用在后台也能持续联网,保证播放的流畅性。
在国产的 ROM 里,要想在后台持续播放,保证应用运行的相关权限给够了才是最稳妥的,所以我们还加了个后台播放优化设置页,这个页面框架里不提供,需要业务自行实现。

通知栏和播控中心

对于通知栏,业务上既有使用系统媒体通知样式的需求,也有使用自定义布局的需求,这些不同样式的通知,基本只有 UI 展示、按钮点击处理上的区别,其他通知逻辑是基本一致的,猫耳播放框架做到了业务只需要设置差异部分,其他 API 调用保持一致。通知基础数据设置如下:
// 音视频通知栏
player.updateNotificationData {smallIcon = R.drawable.ic_player_notificationactionList = arrayListOf(PLAYER_NOTIFICATION_ACTION_PLAY,PLAYER_NOTIFICATION_ACTION_PAUSE,PLAYER_NOTIFICATION_ACTION_PREVIOUS,PLAYER_NOTIFICATION_ACTION_NEXT,PLAYER_NOTIFICATION_ACTION_FAST_FORWARD,PLAYER_NOTIFICATION_ACTION_REWIND)showActionsInCompactView = arrayListOf(1, 2, 3)contentAction = AppConstants.PLAY_ACTIONcontentClassName = MainActivity::class.java.namebizType = PLAYER_FROM_MAINgroupId = NotificationChannels.Play.groupIdchannelId = NotificationChannels.Play.channelIdchannelName = NotificationChannels.Play.channelNamechannelDesc = NotificationChannels.Play.channelDescriptionvisibility = NotificationCompat.VISIBILITY_PUBLIC
}
// 直播通知栏
updateNotificationData {smallIcon = R.drawable.ic_notification_smallforceOngoing = truecustomLayout = R.layout.layout_notification_live_meplayercoverRadius = 4defaultCover = R.drawable.notification_live_default_avatarcontentAction = AppConstants.PLAY_ACTIONcontentClassName = MainActivity::class.java.namebizType = PLAYER_FROM_LIVEgroupId = NotificationChannels.Live.groupIdchannelId = NotificationChannels.Live.channelIdchannelName = NotificationChannels.Live.channelNamechannelDesc = NotificationChannels.Live.channelDescriptionvisibility = NotificationCompat.VISIBILITY_PUBLIC
}
对于播控的适配主要是要考虑 MIUI、ColorOS 等国产 ROM 和鸿蒙的差异,除鸿蒙之外,基本按官方文档更新 MediaSession 即可,对于鸿蒙则要多一些适配,比如鸿蒙支持下图两种场景:

这里面歌词、收藏、快进快退等逻辑都是需要根据不同的业务设置来处理的,目前业务只需要调用播放器对应的字段进行设置即可,使用比较简单。
总结
本文介绍了猫耳FM在 Android 平台上开发媒体播放框架的实践经验,包括架构设计、核心技术、优化改进等方面。希望通过这篇文章,能够给广大的 Android 开发者提供一些有用的参考和启发,也欢迎大家提出宝贵的意见和建议。
Android 学习笔录
Android 性能优化篇:https://qr18.cn/FVlo89
Android Framework底层原理篇:https://qr18.cn/AQpN4J
Android 车载篇:https://qr18.cn/F05ZCM
Android 逆向安全学习笔记:https://qr18.cn/CQ5TcL
Android 音视频篇:https://qr18.cn/Ei3VPD
Jetpack全家桶篇(内含Compose):https://qr18.cn/A0gajp
OkHttp 源码解析笔记:https://qr18.cn/Cw0pBD
Kotlin 篇:https://qr18.cn/CdjtAF
Gradle 篇:https://qr18.cn/DzrmMB
Flutter 篇:https://qr18.cn/DIvKma
Android 八大知识体:https://qr18.cn/CyxarU
Android 核心笔记:https://qr21.cn/CaZQLo
Android 往年面试题锦:https://qr18.cn/CKV8OZ
2023年最新Android 面试题集:https://qr18.cn/CgxrRy
Android 车载开发岗位面试习题:https://qr18.cn/FTlyCJ
音视频面试题锦:https://qr18.cn/AcV6Ap
相关文章:
猫耳 Android 播放框架开发实践
概述 猫耳FM是中国最大的 95 后声音内容分享平台,是B站重要平台之一,深度合作国内顶级声优工作室,打造了数百部精品广播剧,全站播放总量超过百亿次。 MEPlayer 是猫耳 Android 技术团队研发的一款适用于音视频、直播、特效播放等…...
linux下df -h 命令一直卡住的解决方法
在Linux中,偶尔遇到用 df -h 查看磁盘情况时,一直卡住无法显示结果。 解决方法: 1、首先使用strace追踪到底执行到哪里卡住 $ strace df -h 2、如果没有strace命令则进行安装 $ yum install strace -y 3、显示出卡住的地方,如…...
系统架构设计热点知识
系统架构设计师考点包括以下内容: 1. 系统设计和架构思想. 了解系统设计和架构的基本概念和思想,特别是面向服务架构(SOA)、微服务架构、云架构、事件驱动架构、响应式架构等。 系统设计是指在软件项目中,确定系统结…...
2023-在mac下安装Homebrew的国内镜像
mac安装Homebrew的国内镜像 尝试使用其他下载源:GitHub 可能会受到访问限制,尝试使用其他镜像或下载源。您可以使用清华大学、中科大或阿里云的 Homebrew 镜像,以提高下载速度和可靠性。例如,可以使用阿里云的镜像来安装 Homebre…...
Ubuntu 20.04设置虚拟内存 (交换内存swap)解决内存不足
数据库服务器程序在运行起来之后,系统内存不足。 在系统监控中发现,当数据库服务程序启动后,占用了大量内存空间,导致系统的剩余的内存往往只有几十MB。 在ubuntu系统中,swap空间就是虚拟内存,所以考虑在磁…...
RabbitMQ-死信交换机和死信队列
1. 简介 1.1 DLX简介 DLX: Dead-Letter-Exchange 死信交换器,死信邮箱 当消息成为Dead message后,可以被重新发送到另一个交换机,这个交换机就是DLX。 如下图所示: 其实死信队列就是一个普通的交换机,有些队列的消息…...
[HNCTF 2022 WEEK2]easy_include 文件包含遇上nginx
这道纯粹记录 完全没想到 <?php //WEB手要懂得搜索if(isset($_GET[file])){$file $_GET[file];if(preg_match("/php|flag|data|\~|\!|\|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\_|\|\/i", $file)){die("error");}include($file); }else{highlight_file(__…...
python中transform和apply的区别是什么
文章目录 1. 介绍transform:apply: 2. 应用示例示例数据使用transform进行向量化操作使用apply进行更复杂的操作性能比较 3. 示例输出使用 transform 进行向量化操作使用 apply 进行更复杂的操作 4. transform再举例示例数据使用transform计算平均销售额…...
TCP 协议
文章目录 协议格式1面向连接:1.1三次握手(建立连接)1.2包序管理1.2四次挥手(断开连接) 2可靠传输:一。保证数据可靠有序的到达对端:确认应答机制超时重传机制 二。提高传输效率:1.提升自身发送数据量滑动窗口机制 rwnd滑动窗口丢包…...
Azure机器学习 - 在 Azure 机器学习中上传、访问和浏览数据
目录 一、环境准备二、设置内核三、下载使用的数据四、创建工作区的句柄五、将数据上传到云存储空间六、访问笔记本中的数据七、创建新版本的数据资产八、清理资源 机器学习项目的开始阶段通常涉及到探索性数据分析 (EDA)、数据预处理(清理、特征工程)以…...
新建包含cuda和cudnn的docker
背景:服务器的cudnn版本太低了,没有权限去修改。故新建包含cuda和cudnn的docker 步骤 一、拉取镜像及创建docker 拉取相关的镜像 从镜像列表选出相关版本的镜像https://gitlab.com/nvidia/container-images/cuda/blob/master/doc/supported-tags.md …...
Opensips安装配置(以下操作均已centOS 6.3系统为准)
1. 安装依赖软件: a) Yum update //更新系统到最新 b) 安装以下所需依赖软件 gcc bison flex make openssl libmysqlclient-dev mysql-server c) 安装radiusclient: 1. wget http://pkgs.repoforge.org/radiuscli…...
第03章 用户与权限管理
第03章 用户与权限管理 1. 用户管理 1.1 登录MySQL服务器 启动MySQL服务后,可以通过mysql命令来登录MySQL服务器,命令如下: mysql –h hostname|hostIP –P port –u username –p DatabaseName –e "SQL语句"-h参数后面接主机…...
赋能制造业高质量发展,释放采购数字化新活力——企企通亮相武汉2023国际智能制造创新论坛
摘要 “为应对成本上升、供应端不稳定、供应链上下游协同困难、决策无数据依据等问题,利用数字化手段降本增效、降低潜在风险十分关键。在AI等先进技术发展、供应链协同效应和降本诉求等机遇的驱动下,采购供应链数字化、协同化成为企业激烈竞争的优先选…...
洗地新天花板:CEYEE希亦顶配机皇T800 Pro洗地机多点发力上市开售
2023年11月1日,CEYEE希亦正式发布高端清洁产品无线洗地机希亦T800 PRO,创新性地实现了洗地场景深度清洁体验的新突破,彻底解决了清洁行业20多年来技术发展难题,颠覆式引领行业向水汽混动时代迈进,推动了整个市场向“智…...
如何创建一个react项目
文章目录 前言前言打开小黑窗口npm init vite后言 前言 hello world欢迎来到前端的新世界 😜当前文章系列专栏:react.js 🐱👓博主在前端领域还有很多知识和技术需要掌握,正在不断努力填补技术短板。(如果出现错误&am…...
面试算法49:从根节点到叶节点的路径数字之和
题目 在一棵二叉树中所有节点都在0~9的范围之内,从根节点到叶节点的路径表示一个数字。求二叉树中所有路径表示的数字之和。例如,图8.4的二叉树有3条从根节点到叶节点的路径,它们分别表示数字395、391和302,这3个数字…...
http1,https,http2,http3总结
1.HTTP 当我们浏览网页时,地址栏中使用最多的多是https://开头的url,它与我们所学的http协议有什么区别? http协议又叫超文本传输协议,它是应用层中使用最多的协议, http与我们常说的socket有什么区别吗? …...
stable-diffusion-webui环境部署
stable-diffusion-webui环境部署 1. 环境创建2. 安装依赖库3.下载底模4. 获取lora参数文件5.运行代码6. 报错信息报错1报错2 1. 环境创建 创建虚拟环境 conda create -n env_stable python3.10.0进入虚拟环境 conda activate env_stableclone源码 git clone https://github.com…...
使用Ansible中的playbook
目录 1.Playbook的功能 2.YAML 3.YAML列表 4.YAML的字典 5.playbook执行命令 6.playbook的核心组件 7.vim 设定技巧 示例 1.Playbook的功能 playbook 是由一个或多个play组成的列表 Playboot 文件使用YAML来写的 2.YAML #简介# 是一种表达资料序列的格式,类似XML #特…...
RestClient
什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端,它允许HTTP与Elasticsearch 集群通信,而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级ÿ…...
未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?
编辑:陈萍萍的公主一点人工一点智能 未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战,在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...
盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来
一、破局:PCB行业的时代之问 在数字经济蓬勃发展的浪潮中,PCB(印制电路板)作为 “电子产品之母”,其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透,PCB行业面临着前所未有的挑战与机遇。产品迭代…...
376. Wiggle Subsequence
376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...
SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现
摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序,以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务,提供稳定高效的数据处理与业务逻辑支持;利用 uniapp 实现跨平台前…...
python如何将word的doc另存为docx
将 DOCX 文件另存为 DOCX 格式(Python 实现) 在 Python 中,你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是,.doc 是旧的 Word 格式,而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...
图表类系列各种样式PPT模版分享
图标图表系列PPT模版,柱状图PPT模版,线状图PPT模版,折线图PPT模版,饼状图PPT模版,雷达图PPT模版,树状图PPT模版 图表类系列各种样式PPT模版分享:图表系列PPT模板https://pan.quark.cn/s/20d40aa…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...
C#中的CLR属性、依赖属性与附加属性
CLR属性的主要特征 封装性: 隐藏字段的实现细节 提供对字段的受控访问 访问控制: 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性: 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑: 可以…...
深度学习水论文:mamba+图像增强
🧀当前视觉领域对高效长序列建模需求激增,对Mamba图像增强这方向的研究自然也逐渐火热。原因在于其高效长程建模,以及动态计算优势,在图像质量提升和细节恢复方面有难以替代的作用。 🧀因此短时间内,就有不…...
