[Linux Audio Driver] ACDB文件加载流程(完结篇)

高通 AudioReach 框架简介 从实现上,AudioReach 的 Linux ASoC 驱动是 AudioReach 能力的代理,或者适配器,它通过核间通信机制和 DSP 上运行的 AudioReach 通信,对于 Linux 内核,它实现 ASoC 的 PCM 和 compress 接口,向用户空间提供访问音频能力必不可少设备文件等接口。除了运行于 DSP 的 AudioReach 和它的 Linux ASoC 驱动程序,拓扑配置是整个子系统运行的另一个组成部分,Audioreach 的拓扑配置文件可以参考。 阅读详情
AI摘要

本文深入剖析了Android系统中ACDB文件的加载流程,详细介绍了acdb_loader_init_v2函数如何通过acdb_load_files加载ACDB文件,以及get_files_from_properties和get_files_from_device_tree函数在ACDB文件搜索中的作用。

0 背景

首先代码还是android7,之前两篇文章提到,拿到声卡名字以及在platform.c里面通过dlsym的方式,加载acdb_loader_init_v2函数, 今天继续分析;
代码路径:

vendor\qcom\proprietary\mm-audio\audio-acdb-util\acdb-loader\src\family-b\acdb-loader.c

int acdb_loader_init_v2(char *snd_card_name, char *cvd_version, int metaInfoKey){
…
{

1. 简单注解

int acdb_loader_init_v2(char *snd_card_name, char *cvd_version, int metaInfoKey)
{
	int				ret = 0;
	int				i;
	int				result = 0;
	AcdbVocProcGainDepVolTblSizeV2CmdType	vocvoltablesize;
	AcdbSizeResponseType		response;
	AcdbInitCmdType			acdb_init_cmd; //重点结构,后面分析
	AcdbGetMetaInfoSizeCmdType metaInfoSizeCmd;
	AcdbSizeResponseType metaInfoSize;

        pthread_mutex_lock(&loader_mutex); //上锁
        if (acdb_init_ref_cnt != 0) {
            acdb_init_ref_cnt++;
            ALOGD("ACDB -> already initialized, exit");
            goto done;
        }

	list_init(&aud_vol_idx_list.list); // 链表初始化,acdb id,app id相关,

	if (acdb_load_files(&acdb_init_cmd, snd_card_name) <= 0) { //acdb_load_files是加载
	                                                           //acdb文件的重点
		LOGE("ACDB -> Could not load .acdb files!\n");
		ret = -ENODEV;
		goto done;
	}

	LOGD("ACDB -> ACDB_CMD_INITIALIZE_V2\n");
	ret = acdb_ioctl(ACDB_CMD_INITIALIZE_V2,   //初始化acdb文件
		(const uint8_t *)&acdb_init_cmd, sizeof(acdb_init_cmd), NULL, 0);
    ......
	acdb_rtac_init(); //接收/发送来自QACT的数据,并将其发送/接收到RTAC驱动程序
	                  //(用于设备和QACT通信)
    ......
	acdb_loader_send_common_custom_topology(); //加载音频校准的API的数据并推送到DSP

	if (send_meta_info(metaInfoKey) < 0)
		LOGD("ACDB -> send_meta_info failed");

	current_feature_set = ACDB_VOCVOL_FID_DEFAULT;
	is_initialized = true; //bool变量,acdb加载一波之后置true

	parse_codec_type(snd_card_name); //声卡类型解析

	LOGD("ACDB -> init done!\n");
        acdb_init_ref_cnt++;

done:
        pthread_mutex_unlock(&loader_mutex); //释放锁
	return ret;
}

2. 重点分析

if (acdb_load_files(&acdb_init_cmd, snd_card_name) <= 0)

这句代码是加载acdb 的核心,我们先看一下这个变量acdb_init_cmd;

AcdbInitCmdType acdb_init_cmd;

//Structure to hold an individual ACDB file name and path.
LA.UM.5.6\vendor\qcom\proprietary\mm-audio\audcal\family-b\acdb\inc\acdb.h

struct _AcdbFileName{
   uint32_t fileNameLen;
   //< Full file path name length.
   char fileName[ACDB_FILENAME_MAX_CHARS];
     //< Array that holds the ACDB file path and name. The file size cannot
       //   exceed 256 characters, including the NULL-termiated character.
          //@newpagetable 
}

typedef struct _AcdbInitCmdType AcdbInitCmdType;
#include "acdb_begin_pack.h"


 //  Query command structure for the command ACDB_CMD_INITIALIZE_V2.

struct _AcdbInitCmdType {
   uint32_t nNoOfFiles;
  //< Number of ACDB files to read from the acdbFiles array. 
   AcdbFileName acdbFiles[20];
    //< Array of ACDB file names. A maximum of 20 ACDB files can be
         // provided at one time to be initialized. 
}

我们可以看到acdb_init_cmd是AcdbInitCmdType结构类型的,其存储了ACDB的文件名字以及
ACDB文件的位置;

接下来进去acdb_load_files函数,

vendor\qcom\proprietary\mm-audio\audio-acdb-util\acdb-loader\src\family-b\acdb-loader.c
static int acdb_load_files(AcdbInitCmdType *acdb_init_cmd, char * snd_card_name)
{
	int result = 0;

	result = get_files_from_properties(acdb_init_cmd);
	if (result > 0)
		goto done;

	result = get_files_from_device_tree(acdb_init_cmd, snd_card_name);
done:
	return result;
}

先分析get_files_from_properties函数,

vendor\qcom\proprietary\mm-audio\audio-acdb-util\acdb-loader\src\family-b\acdb-loader.c
static int get_files_from_properties(AcdbInitCmdType *acdb_init_cmd)
{
	int i = 0;
	int prop_len;
	char prop_name[24];

	for (i=0; i < MAX_ACDB_FILES; i++) {
		if (snprintf(prop_name, sizeof(prop_name), "persist.audio.calfile%d", i) < 0)
			goto done;

		prop_len = property_get(prop_name, acdb_init_cmd->acdbFiles[i].fileName, NULL);
		if (prop_len <= 0)
			goto done;

		acdb_init_cmd->acdbFiles[i].fileNameLen = 
		          strlen(acdb_init_cmd->acdbFiles[i].fileName);
		LOGD("ACDB -> Prop Load file: %s\n", acdb_init_cmd->acdbFiles[i].fileName);
	}
done:
	acdb_init_cmd->nNoOfFiles = i;
	return i;
}

这段代码很奇怪,首先这个persist.audio.calfile默认代码就没有配置(当然可能是作为调试用代码),
从我的理解来看,他会通过snprintf把"persist.audio.calfile0"存到字符数组prop_name里面去,

#define MAX_ACDB_FILES		20

这个常量最大20,所以字符最多为"persist.audio.calfile20",最大占用23个char 型位置,字符串最后补个\0的话,刚刚数组长度为24,那么其输出必然不被截断,返回值固定为24;

之后代码又通过property_get的方式,把刚才存到prop_name的值存到acdb_init_cmd->acdbFiles[i].fileName里面,而我们从刚才的重点的那个结构AcdbInitCmdType,

struct _AcdbInitCmdType {
   uint32_t nNoOfFiles;
  //< Number of ACDB files to read from the acdbFiles array. 
   AcdbFileName acdbFiles[20];
    //< Array of ACDB file names. A maximum of 20 ACDB files can be
         // provided at one time to be initialized. 
}

可以看出(从注释也行),其明明应该存储acdb 文件名字,所以这段代码很诡异,当时实际调试由于这个属性persist.audio.calfilem没有配置(从adb shell 进入设备,然后getprop persist.audio.calfilem没有返回值)可以看出,这段代码根本没有走到;

然后我们回到static int acdb_load_files(AcdbInitCmdType *acdb_init_cmd, char * snd_card_name)这个函数继续分析:

result = get_files_from_device_tree(acdb_init_cmd, snd_card_name);

先把这个 主要贴下来(只能删减写,不然这篇还写不完,发现优快云有篇幅限制),如下:

static int get_files_from_device_tree(AcdbInitCmdType *acdb_init_cmd, char *snd_card_name)
{
	int result = 0;
	char dir_path[300];
	char board_type[64] = DEFAULT_BOARD;
	FILE *fp = NULL;

	/* Get Board type */
	fp = fopen("/sys/devices/soc0/hw_platform","r");
	if (fp == NULL)
		fp = fopen("/sys/devices/system/soc/soc0/hw_platform","r");
	if (fp == NULL)
		LOGE("ACDB -> Error: Couldn't open hw_platform\n");
	else if (fgets(board_type, sizeof(board_type), fp) == NULL)
		LOGE("ACDB -> Error: Couldn't get board type\n");
	else if (board_type[(strlen(board_type) - 1)] == '\n')
		board_type[(strlen(board_type) - 1)] = '\0';
	if (fp != NULL)
		fclose(fp);

	/* get files from form factor & soundcard independant path */
	result = get_acdb_files_in_directory(acdb_init_cmd, ACDB_BIN_PATH);
	if (result >= 0)
		LOGD("ACDB -> found %d form factor & soundcard independant files\n",
			result);
	if (result > MAX_INDEPENDANT_ACDB_FILES)
		goto done;

	/* Try board directory with soundcard name */
	if (snd_card_name != NULL) {
		result = snprintf(dir_path, sizeof(dir_path), "%s/%s/%s", 
		ACDB_BIN_PATH, board_type, snd_card_name);
		if (result < 0) {
			LOGE("ACDB -> Error: snprintf failed for snd card %s, error: %d\n", 
			snd_card_name, result);
			result = -ENODEV;
			goto done;
		}
		result = get_acdb_files_in_directory(acdb_init_cmd, dir_path);
		if (result > 0)
			goto done;
	}

	......
}

高通代码上来就给你个注释:

/* Get Board type */
fp = fopen("/sys/devices/soc0/hw_platform","r");
if (fp == NULL)
	fp = fopen("/sys/devices/system/soc/soc0/hw_platform","r");
if (fp == NULL)
	LOGE("ACDB -> Error: Couldn't open hw_platform\n");
else if (fgets(board_type, sizeof(board_type), fp) == NULL)
	LOGE("ACDB -> Error: Couldn't get board type\n");
else if (board_type[(strlen(board_type) - 1)] == '\n')
	board_type[(strlen(board_type) - 1)] = '\0';
if (fp != NULL)
	fclose(fp);

board_type,这个代码里面默认配置的是MTP:

~/LA.UM.5.6$ grep -nr "DEFAULT_BOARD" ./vendor/ ./hardware/
./vendor/qcom/proprietary/mm-audio/audio-acdb-util/acdb-loader/Android.mk:17:
libacdbloader-def += -D DEFAULT_BOARD=\"MTP\"

./vendor/qcom/proprietary/mm-audio/audio-acdb-util/acdb-loader/src/family-b/acdb-loader.c
:616:  char board_type[64] = DEFAULT_BOARD;

然后经过fopen的读取之后,我们能够获取到他的值是QRD,这玩意是高通CPU的平台信息,芯片内部
有一段空间是存储这个平台信息的,抽空俺也可以分析一波(之前有了解,但是忘了,信息好像是从
modem那边传过来的);随便找台设备,cat 一下这个节点值,我们就知道是QRD了。
在这里插入图片描述

/* get files from form factor & soundcard independant path */
result = get_acdb_files_in_directory(acdb_init_cmd, ACDB_BIN_PATH);
if (result >= 0)
	LOGD("ACDB -> found %d form factor & soundcard independant files\n",
		result);
if (result > MAX_INDEPENDANT_ACDB_FILES)
	goto done;

后面的代码几乎都是get_acdb_files_in_directory函数在跑了,

ACDB_BIN_PATH在android 7是 设备里面的/etc/acdbdata/

result = get_acdb_files_in_directory(acdb_init_cmd, ACDB_BIN_PATH);

这个代码的意思就是,先在设备里面的etc/acdbdata/目录下面找,能不能找到那八个文件?(一般是8个),
#更新 2022.02.08
春节前去高通上海参加了培训,他们新一代的AudioReach架构,acdb文件只有一个了,即集成到一个文件中。
#更新 2022.02.08

找到了goto done;函数跑完,找不到,走下一个判断;

result = snprintf(dir_path, sizeof(dir_path), "%s/%s/%s", 
              ACDB_BIN_PATH, board_type, snd_card_name);

接下来看你能否在/etc/acdbdata/QRD/msm8953-sku3-snd-card下面找到这个acdb文件;之后就是依次查找: /etc/acdbdata/QRD/ -> /etc/acdbdata/MTP/msm8953-sku3-snd-card ->
/etc/acdbdata/MTP -> /etc

总之,按照这个流程在设备里面找这个参数,按代码顺序,谁先找到就加载那个位置的acdb文件。

补充: 这个acdb的文件由系统编到设备目录下;

3. 总结与引申

其实整个acdb 文件的加载流程如下图所示:

在这里插入图片描述
(备注,图片的标号略有问题,俺就懒得改了…)

那么我们知道这个有啥用呢,其实在实际研发过程中,会遇到不得不使用同一套软件而兼容不同硬件状态的情况,具体可参考下面这位博主的博客,我就不多聊了。

https://blog.youkuaiyun.com/crow_ch/article/details/103886156

4. 作者注

/******
@article{Linux Audio Driver,
Author = { 1byte ≠ 8bit},
Year = { 2020},
}
******/
从AGM到ADSP:图解高通AudioReach架构下的一次PCM_READ完整旅程 本文深入解析了高通8650平台AudioReach架构下PCM_READ的完整数据流,从用户空间请求到ADSP处理的每个关键环节。详细介绍了AGM、GSL和GPR等核心组件的协作机制,以及如何优化音频处理性能,为开发者提供了实用的调试和调优指南。 阅读详情

相关推荐

[Linux Audio Driver] ACDB文件加载路径解析与多硬件兼容实战

本文深入解析了Linux音频驱动中ACDB文件的加载路径与多硬件兼容实战技巧。ACDB文件作为音频校准数据库,对硬件兼容性至关重要。文章详细介绍了加载路径的优先级机制、多硬件适配的关键代码及调试技巧,并探讨了新一代AudioReach架构的变化,帮助开发者解决音频驱动兼容性问题。

weixin_30036289的博客 813

Qualcomm audio培训

高通audio培训。

编程菜汝狗 3962

Linux Audio Driver 排错:acdb id 10 导致喇叭无声的 2 种定位与修复

本文深入解析Linux音频驱动中acdb id 10导致喇叭无声的问题,提供两种有效解决方案:修正DTS硬件检测配置和强制覆盖acdb ID。通过详细的技术分析和实战案例,帮助开发者快速定位和修复音频路由问题,适用于高通平台的嵌入式Linux系统开发。

weixin_29173777的博客 237

二、Audio 架构

Audio架构图 从上图看出,共分为高中低三层 **高层: AVaudioplayer** audio 基本操作:播放 暂停 停止 循环 时间控制:任意时间位置播放、进度控制 从文件或者缓冲区播放 获取音视频关键参数AVaudioqueue 播放录制、可以拿到音频原始处理数据 1、初始化音频管理队列 2、音频队列:管理回调方法 3、实例化音频队列:audioqueueoutpu

flover5724059的博客 1236

【Audio Driver】ACDB文件加载

ADCB文件分析 文件目录 加载流程 文件信息存放在哪 文件名 解析后的数据 解析acdb文件函数 ACDB Loader acdb文件如何修改 补充 wsa状态判断 acdb文件分析路径分析 hwdep_send_cal函数处理 如何获取acdb文件 1 acdb介绍 参考文章:qcom 音频相关的dsp driver笔记(基于msm8996平台) 这个文章介绍的特别详细。 acdb,全称:au...

qq_38091632的博客 1万+

Qualcomm AudioReach音频架构

【代码】Qualcomm AudioReach音频架构。

sdrgtywretyeruet的专栏 1624

[Linux Audio Driver] ACDB文件加载流程(二)

1.概述 上篇文章,我们通过分析snd_card_name = mixer_get_name(adev->mixer);这句代码,我们拿到了声卡名字,详情见以下超链接: ACDB文件加载流程(一) 然后今天接着分析void *platform_init(struct audio_device *adev)函数,探讨acdb是如何被加载的, 其代码路径是: hardware\qcom\audi...

编程菜汝狗 3254

高通音频驱动调试:SLIMbus配置与ACDB加载实战

SLIMbus是高通平台专用的低功耗音频总线,其控制器初始化是音频子系统启动的前提;ACDB(Audio Calibration Database)则承载硬件级声学校准参数,决定codec能否正确初始化。理解SLIMbus主从通信机制、设备树地址映射规则及ACDB的HWID匹配逻辑,是打通Linux内核侧音频通路的关键。该技术广泛应用于车载信息娱乐(IVI)、智能座舱(如SA8155)、AR/VR音频模块等嵌入式场景。本文聚焦SLIMbus设备树使能、ACDB二进制加载验证与machine driver绑定

weixin_42525632的博客 209

[Linux Audio Driver] ACDB文件加载流程(一)

声卡信息是acdb加载中最重要的信息,本文追踪代码,解释声卡名如何被取出来使用的; 环境: Qualcomm平台,android7 , 内核版本:msm-3.18 1. platform_init函数 代码路径: hardware/qcom/audio/hal/msm8916/platform.c void *platform_init(struct audio_device *adev) ...

编程菜汝狗 5397

高通audio入门

介绍了高通audio入门 1.Audio软硬件系统框架 2.Audio bringup 3.ACDB校准 4.how to debug

高通平台音频应用指导_V1.0(初稿).pdf

移远 高通平台音频应用指导

[Linux Audio Driver] 高通音频相关汇总(持续更新)

0. 背景 之前写了一些音频的博客,但是比较散乱,就想着汇总下写的博客,也是对自己知识结构的梳理,这个会持续更新。 1. 代码分析&总结 I2S相关介绍 ACDB文件加载流程(一) ACDB文件加载流程(二) ACDB文件加载流程(完结篇) 高通平台内部MIC_BIAS简介 从设备地址理解 Android7 外部PA Speaker代码结构 MSM8953音频部分设备树调用关系 开启音频kernel内核debug功能 声卡与XML匹配流程 SDM660平台内部CODEC tinyalsa命令总结 S

编程菜汝狗 4962

论文精读——A Comprehensive Survey of Continual Learning: Theory, Method and Application

为了应对现实世界的动态性,智能系统需要在其整个生命周期中逐步获取、更新、积累并利用知识。这种能力被称为Continue Learning(持续学习)。从广义上讲,持续学习明显受到catastrophic forgetting(灾难性遗忘)的限制,即学习新任务通常会导致旧任务的性能急剧下降。Continue Learning的核心目的有二,其一为(稳定性与可塑性间的权衡),其二为(足够的任务内/任务间泛化能力)。

xsddys的博客 1241

高通Audio中ASOC的codec驱动 -----mark

高通Audio中ASOC的codec驱动(二) 原文:https://www.tinymind.net.cn/articles/8c917351355016 继上一篇文章:高通Audio中ASOC的machine驱动(一) ASOC的出现是为了让codec独立于CPU,减少和CPU之间的耦合,这样同一个codec驱动就无需修改就可以匹配任何一款平台。 在Machine中已经知道,snd...

weixin_42082222的博客 1965

基于Zynq的便携式γ能谱仪设计与实现

数字多道分析器(DMCA)是核辐射能谱测量的核心设备,其本质是将探测器输出的模拟脉冲按幅度数字化分道,构建能量-计数分布曲线,从而支撑放射性核素识别与定量分析。其技术原理依赖高速采样、数字滤波成形、基线恢复与峰值提取等关键信号处理环节,具备高分辨率、优线性度与强抗干扰能力。在环境监测、核安全巡检与野外地质勘探等场景中,对低功耗、小型化、实时性提出严苛要求。本方案采用Xilinx Zynq-7020异构SoC架构,融合FPGA实时数字信号处理与ARM嵌入式控制,实现软硬协同优化,兼顾算法精度与工程落地性,为国

weixin_33173126的博客 190

保姆级教程:用Python和PyTorch复现PTB-XL心电信号分类论文(附完整代码)

本文提供了一份详细的PTB-XL心电信号分类教程,涵盖从数据预处理到模型优化的完整流程。通过PyTorch实现三种深度学习架构(Conv1D、SincNet和熵特征融合网络),并针对医疗AI特有的类别不平衡问题提供解决方案。教程包含可运行的代码示例,帮助开发者快速复现论文结果并应用于实际心电信号分析场景。

weixin_30555125的博客 405

高通8650 AudioReach架构中的GSL-Passthru-GPR推理过程

在分析audio-pkt.c(passthru)这个文件的代码时,我们推断它实现了一个名为aud_passthru_adsp的字符设备。原生tinyalsa pcm_open操作后端注册的dai link,把codec前后端连起来,还有codec上电,数据走agm。audio_prm.c 提供时钟控制接口,比如开关的功能,或者介于开关的feature调整。函数属性,实现了从应用处理器 (AP) 到后端驱动的通信。是一个包含函数指针的结构体,其中一个重要的函数指针是指向。的代码位于gpr_lx.c文件的。

常来看看~ 1829

【Codex Sandbox 与审批机制:小白也能看懂的实用教程】

这篇教程介绍了Codex工具中的沙盒(Sandbox)和审批(Approvals)机制,帮助用户安全高效地使用Codex协作。沙盒限制Codex访问范围,审批机制则控制敏感操作权限。文章详细讲解了五种常见模式:最安全的安全只读浏览、只读非交互、仓库内编辑、Auto预设和不推荐的YOLO模式。还提供了新手使用路径建议、配置示例和实战场景范式,包括如何测试沙盒行为、不同系统下的实现方式以及常见问题解答。最后附上速查清单,帮助用户快速选择合适的权限组合。

zsr154278963的博客 6706

音频 (一) AudioRecord 架构简介

    去年做了关于蓝牙音频这一块,当时没有进行总结,现在再去总结都忘了差不多了,但是不管怎么样还是进行总结一下吧!总结过程中有什么不对的地方请大家不吝指教。音频基础知识声音有哪些重要属性呢?响度(Loudness)响度就是人类可以感知到的各种声音的大小,也就是音量。响度与声波的振幅有直接关系。音调(Pitch)音调与声音的频率有关系,当声音的频率越大时,人耳所感知到的音调就越高,否则就越低。音色...

Tony-jiang的博客 8470

CAN轴控制流【6041,6040】

【代码】CAN轴控制流【6041,6040】

cfqq1989的博客 1783

微信支付PHP集成指南

完整实现建议参考微信支付官方文档,包含更多支付场景(H5支付、Native支付等)和安全性处理细节。微信支付在PHP中的集成主要涉及服务端接口的调用和支付流程的实现。

bqq506615054的专栏 158

高通8650 AudioReach 架构中 Virtual Mic & Loopback

然而,由于平板移除了自带的扬声器,在 DP in 场景下,平板需要使用座舱的扬声器。答:上层的任何应用程序在获取录音时,最终都会通过 Android 的标准 API 调用到 AudioFlinger,从 HAL 获取录音。上面提到,DP in 被用作录音设备。在实际方案讨论中,有人建议在 HAL 中添加 IPC,如果选择在 HAL 中添加 IPC,就需要找到 pcm_read 的位置进行场景区分。由于 DP in 是一种特殊的录音设备,专供特定应用使用,不需要开放给其他第三方应用,因此这个方案是可行的。

常来看看~ 2332

【机器人技术基础】MATLAB Robotics Toolbox PUMA560 运动学正逆解

机器人技术基础实验2报告 1. 实验目的和要求 2. 实验手段 2.1 实验原理 2.2 函数详解 3. 实验主要内容 4. 实验结果和分析 4.1 正运动学求解 例1 例2 4.2 逆运动学求解 例1 例2 1. 实验目的和要求 掌握常见形式的机器人的D-H坐标系建立方法、运动学建模; 正逆运动学分析与求解; 使用Robotics Toolbox对直角坐标机器人、圆柱坐标机器人、球坐标机器人、PUMA560其中之一建立运动学模型,正逆求解和仿真。 2. 实验手段 2.1

【人工智障 | 计算鸡视觉 | 机器孩】 1万+

Audio框架层理解

http://blog.youkuaiyun.com/gowyz/article/details/6019314

xuwenwu1985的博客 608

uni-app - 文本展开 / 收起折叠功能,支持自定义样式(当文本内容超出规定行数后,展开收起折叠的功能)兼容 H5 / App / 小程序且易用更容易修改的插件组件源码,超详细的示例代码及注释

uniapp实现文本的展开与收起功能,uniapp展开全部和收起,uniapp里文本的展开与收起,uniapp中文本内容超出规定行数后展开收起的处理,UNIAPP兼容h5、微信小程序多行文本展开或收起,uni-app中,文字超出隐藏并显示省略号(实现展开、收起全文),uni-app中文本过长-实现折叠与展开效果,中文本内容超出规定行数(字数)后展开收起的处理,超出部分...显示,uniapp适用于uniapp多行文本溢出显示...,多行文本展开或收起,uni-app-文本展开/收起折叠功能,支

🏆 前端领域优质创作者 5641

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

Android平台实现系统内录(捕获播放的音频)并推送RTMP服务技术方案探究

低版本的Android系统,方案1应该是相对可行但局限很大的选择,方案2大多时候,非定制设备,很难满足权限要求,方案3对Android系统版本要求比较高。通过测试,方案3除了对Android版本要求比较高外,体验式最好的,感兴趣的开发者,可以尝试看看,如果是特定场景下,本身选用的设备,Android的版本就比较高,又有内录audio需求的话,无疑是非常不错的选择。

大牛直播SDK 3669

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

上一篇: [Linux Audio Driver] PDM CLOCK导致的耳机右声道POP音问题
下一篇: [C++基础] 在线C++编译器推荐
1byte ≠ 8bit
博客等级 码龄8年 7242粉丝 170原创
评论 7
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值