咨询LHDCV5解码算法
我正在制作ESP32蓝牙播放器,根据Github开源的解码器已经在ESP32上成功跑通了AAC、aptX、LDAC,然后我试图在ESP32上实现LHDCV5,但是注意到目前似乎没人开源过LHDCV5的解码器,我只找到了BES的SDK(包含LHDCV5静态库“.a”文件及对应的api头文件)、AOSP的LHDCV5解码器(缺lhdcv5_util_dec.c文件)、小米手机内置的LHDCV5动态库“.so”文件。因为我不懂逆向,同时注意到AOSP和ESP32的bluedroid架构几乎一样,且其依赖的外部库只是缺一个源文件,所以我从AOSP移植LHDCV5解码器到ESP-IDF,目前这部分工作已经完成,我将他们开源在Github的liblhdc-collections/tree/main/ESP-IDF/bluedroid;它们包含下列文件:
└─bluedroid
├─api
│└─include
│ esp_a2dp_api.h
│
├─external
│└─liblhdcv5dec
│ │CMakeLists.txt
│ │release_note
│ │
│ ├─inc
│ │ lhdcv5BT_dec.h
│ │
│ ├─include
│ │ lhdcv5_util_dec.h
│ │
│ └─src
│ lhdcv5BT_dec.c
│ lhdcv5_util_dec.c
│
└─stack
├─a2dp
│ a2dp_vendor.c
│ a2dp_vendor_lhdcv5.c
│ a2dp_vendor_lhdcv5_decoder.c
│
└─include
└─stack
a2dp_vendor.h
a2dp_vendor_lhdcv5.h
a2dp_vendor_lhdcv5_constants.h
a2dp_vendor_lhdcv5_decoder.h
a2dp_vendor_lhdc_constants.h
因为AOSP需要使用liblhdcv5外部库,所以移植到IDF的LHDCV5解码器也是,因此我也同步移植了AOSP的LHDCV5外部库,但是正如前面所说,这个外部库缺乏lhdcv5_util_dec.c的实现,因此我依据lhdcv5_util_dec.h中声明的函数和使用的变量逆推了一个lhdcv5_util_dec.c,但是其中的关键部分——LHDCV5解码算法我无法得知,我搜索不到任何与LHDC编码算法相关的文档/代码。
所以我在lhdcv5_util_dec.c的LHDCV5解码函数“int32_t lhdcv5_util_dec_process(uint8_t * pOutBuf, uint8_t * pInput, uint32_t InLen, uint32_t *OutLen)”中调用了正弦发生器进行伪解码,实际输出的是正弦发生器产生的标准音。对于这个liblhdcv5dec,我也将其开源在Github:liblhdcv5dec,完整内容可以在这个Github仓库看到,其中的伪解码函数如下:
int32_t lhdcv5_util_dec_process(uint8_t * pOutBuf, uint8_t * pInput, uint32_t InLen, uint32_t *OutLen)
{
if (!decoder_initialized)
return LHDCV5_UTIL_DEC_ERROR_NO_INIT;
if (!pInput || !pOutBuf || !OutLen)
return LHDCV5_UTIL_DEC_ERROR_PARAM;
// 计算样本大小和帧样本数 - 使用输出位深
uint32_t sample_bytes = (g_output_bit_per_sample + 7) / 8;
uint32_t frame_samples = 256;// LHDC V5典型每声道样本数
uint32_t channels = (g_channel_type == LHDC_OUTPUT_STEREO) ? 2 : 1;
*OutLen = frame_samples * sample_bytes * channels;
// 生成32位正弦波PCM数据,然后根据需要转换
int32_t *out_32 = (int32_t *)pOutBuf;
int16_t *out_16 = (int16_t *)pOutBuf;
uint8_t *out_24 = pOutBuf;// 24位特殊处理,存储为3字节
for (uint32_t i = 0; i < frame_samples; i++) {
// 生成左声道样本(始终生成32位样本)
int32_t sample_32 = generate_sine_sample(g_sampleRate, 32);
// 根据输出位深转换
switch (g_output_bit_per_sample) {
case 16:
// 从32位转换到16位
int16_t sample_16 = (int16_t)(sample_32 >> 16);
out_16 = sample_16;// 左声道
if (channels == 2) {
// 右声道使用稍作修改的样本,制造立体声效果
out_16 = (int16_t)(sample_16 * 0.9f); // 右声道
}
break;
case 24:
// 从32位转换到24位
int32_t sample_24 = sample_32 >> 8; // 右移8位得到24位
// 正确处理有符号24位样本
int32_t clamped_sample = sample_24;
// 确保样本在24位范围内
if (clamped_sample > 8388607) clamped_sample = 8388607;
if (clamped_sample < -8388608) clamped_sample = -8388608;
// 转换为无符号偏移量(+8388608)以便正确存储符号位
uint32_t u_sample = clamped_sample + 8388608;
out_24 = u_sample & 0xFF; // 低8位
out_24 = (u_sample >> 8) & 0xFF;// 中8位
out_24 = (u_sample >> 16) & 0xFF; // 高8位
if (channels == 2) {
int32_t right_sample = (int32_t)(sample_24 * 0.9f);
int32_t clamped_right = right_sample;
if (clamped_right > 8388607) clamped_right = 8388607;
if (clamped_right < -8388608) clamped_right = -8388608;
uint32_t u_right = clamped_right + 8388608;
out_24 = u_right & 0xFF;
out_24 = (u_right >> 8) & 0xFF;
out_24 = (u_right >> 16) & 0xFF;
}
break;
case 32:
out_32 = sample_32;// 左声道
if (channels == 2) {
out_32 = (int32_t)(sample_32 * 0.9f);// 右声道
}
break;
}
}
LOG_DEBUG("%s: Generated sine wave frame - %u samples, %u bits, %u channels, %u bytes",
__func__, frame_samples, g_bitPerSample, channels, *OutLen);
return LHDCV5_UTIL_DEC_SUCCESS;
}
目前在ESP32上已经可以运行伪解码的LHDCV5了,小米14连接ESP32后,以LHDCV5协商,手机播放音乐时,ESP32上听到的是正弦发生器的标准音。我想知道我该如何获取LHDCV5的解码算法源码?官方肯定是不行了,早段时间发过邮件没理我。注意到论坛的大神@O11111和@O2C14已经在这方面有进展,非常佩服,也注意到有人说还有别的大神也实现了,但我不知道怎么联系,我是新来的小白一个,不知大神们可否与我交流一下,我挺想在ESP32等设备上实现真正的LHDCV5解码,可以有偿(不是太贵都能接受),不白拿资料:lolGithub的sprlightning可联系到我,还有我Q是3397935446:lol
虽然没有资料,但是给你大大的赞。 jsjj 发表于 2025-11-25 22:51
虽然没有资料,但是给你大大的赞。
哈哈感谢:lol 不知道什么情况,上面写的地址超链接没生效,这里直接把开源地址发出来吧:
1)移植到IDF的liblhdcv5dec源码(伪解码版本):https://github.com/sprlightning/liblhdcv5dec
2)与liblhdcv5dec配套的LHDCV5解码器源码(IDF bluedroid):https://github.com/sprlightning/liblhdc-collections/tree/main/ESP-IDF/bluedroid
3)我收集的所有LHDC方面的资料,包括BES2600IHC、BES2700IHC(含静态库),和Android方面(含64位动态库),也集成了我在使用的IDF内容:https://github.com/sprlightning/liblhdc-collections 是的,我们开发了 LHDCV5 编码器和解码器,但从未公开发布。我只在 Linux 系统下使用打过补丁的 Pipewire 版本进行了测试。我们计划最终发布,但目前存在一些限制,例如不支持 192 kHz(不过反正也没什么用)或无损编码。
O11111 发表于 2025-11-26 01:13
是的,我们开发了 LHDCV5 编码器和解码器,但从未公开发布。我只在 Linux 系统下使用打过补丁的 Pipewire...
感谢大神回复,我对你和O2C14搞定LHDCV5的编解码器算法感到非常钦佩。因为你们计划最终发布,那我就等等,并关注你们的动态:lol
我认为复现LHDCV5的编解码算法应该需要比较好的通信理论基础,我确定我这方面能力不行,可能起不到什么帮助;不过对于LHDCV5的测试我是很期待的,我有支持LHDCV5的XIAOMI14手机和漫步者花再halo space耳机,以及移植codecs的能力,如果大神们有这方面的需求可以找我:lol
此外我有一个疑问,你们已经实现了LHDCV5在低采样率下的编解码,为什么高采样率还不支持?是受限于处理器计算速度?还是什么别的限制?不同采样率情况下编解码算法会有较大差异吗?原谅我的奇怪问题,因为我这方面不是很懂,但是我确实比较好奇~ 不错不错学习一下:) ChayaSar 发表于 2025-11-26 04:42
感谢大神回复,我对你和O2C14搞定LHDCV5的编解码器算法感到非常钦佩。因为你们计划最终发布,那我就等等 ...
最主要的是需要花费大量时间和一些 C 语言知识。现代逆向工程工具让这个过程变得简单得多,甚至不需要懂汇编语言(尽管懂汇编会有帮助)。
目前,96 kHz 的采样率可以正常工作。192 kHz 的采样率需要额外的工作,我不太想做。而且它本身也没什么用,因为没有任何设备支持。它主要涉及一些常量和表格,算法是一样的。另一方面,无损模式则截然不同,而且当时没有任何设备支持它,所以就被跳过了。
此外,LHDCV5 包含大量的浮点运算,所以你需要一个浮点运算单元(FPU)。 O11111 发表于 2025-11-26 18:46
最主要的是需要花费大量时间和一些 C 语言知识。现代逆向工程工具让这个过程变得简单得多,甚至不需要懂 ...
感谢大神解惑:lol 我试了试把BES2700IHC的lhdcv5 a文件(ARM架构)载入IDA,它给我解析出来19个o文件,大致分为:
- 内层lhdc_log;
- 内层svlice_v5系列;
- 内层lhdc_v5系列;
- 外层lhdcv5_util_dec,有配套api头文件;
我用IDA自动反汇编了几个文件,它们的关联性让我很满意,这使我产生出了一个计划:依次逆向19个o文件,合并其中重复的函数,那也许会得到所有完整函数的伪代码。然后按照这些伪代码的逻辑,再复现出所有函数的源码。不过这也许会花很多的时间。
我注意到这些a文件似乎比so文件更完整,更利于分析。lhdcv5 a文件在上面的GitHub开源地址已提供,有同样想法的朋友可以试试:lol
页:
[1]