一个专业的容器化音频转换服务,专门针对微信SILK格式进行深度优化,支持多种音频格式之间的高质量转换。基于Skype SILK SDK v3和FFmpeg 7.1.1构建,提供企业级的音频处理能力。
┌─────────────────────────────────────────────────────────────┐
│ Flask API Layer │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────┐ │
│ │ File Upload │ │ Base64 Input │ │ Health Check│ │
│ └─────────────────┘ └─────────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
┌─────────────────────────────────────────────────────────────┐
│ Audio Processing Engine │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────┐ │
│ │ Format Router │ │ Quality Check │ │ Duration │ │
│ │ (Smart Path) │ │ Validator │ │ Calculator │ │
│ └─────────────────┘ └─────────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
┌─────────────────────────────────────────────────────────────┐
│ Codec Layer │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────┐ │
│ │ SILK Encoder │ │ SILK Decoder │ │ FFmpeg │ │
│ │ (Custom C++) │ │ (Official) │ │ (Static) │ │
│ └─────────────────┘ └─────────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘
- 容器化: Docker多阶段构建 (Builder + Runtime)
- Web框架: Flask 3.x (Python 3.11)
- 音频处理: FFmpeg 7.1.1 (静态编译)
- SILK处理: Skype SILK SDK v3 + 自定义优化编码器
- 编程语言: Python (API层) + C/C++ (编解码器)
- ✅ SILK v3 专业支持:基于官方Skype SDK,完全兼容微信语音格式
- ✅ 智能转换路径:自动选择最优转换策略(直接转换 vs PCM中转)
- ✅ 质量保证机制:多层验证确保转换质量(文件头检查、零字节检测、时长验证)
- ✅ 高精度时长:使用FFprobe获取精确音频时长信息(向上取整)
- ✅ 双输入模式:支持文件上传和Base64编码数据输入
- ✅ 自动资源管理:转换完成后自动清理临时文件
- ✅ 容器化部署:多阶段Docker构建,生产环境优化
- ✅ 详细错误处理:完整的异常捕获和用户友好的错误信息
| 格式 | 扩展名 | 描述 | 采样率支持 |
|---|---|---|---|
| SILK | .silk, .slk |
微信语音格式,Skype开发的低比特率语音编解码器 | 8-48kHz |
| WAV | .wav |
无损音频格式,PCM编码 | 任意 |
| MP3 | .mp3 |
MPEG-1 Audio Layer III,广泛使用的有损压缩格式 | 任意 |
| OGA | .oga |
Telegram语音格式,Ogg Vorbis格式,开源的有损压缩格式 | 任意 |
| 格式 | 编码器 | 参数配置 | 适用场景 |
|---|---|---|---|
| MP3 | LAME | 128kbps, 可变比特率 | 通用音频播放 |
| OGA | Vorbis | 质量等级8, 高质量压缩 | Telegram语音消息 |
| SILK | Custom | 24kHz, 24kbps, 单声道 | 微信语音消息 |
| WAV | PCM | 24kHz, 16-bit, 单声道 | 无损音频存储 |
输入格式 → 输出格式 转换路径 性能等级
─────────────────────────────────────────────────────
WAV/MP3/OGA → MP3/OGA FFmpeg直接转换 ⚡ 高性能
WAV/MP3/OGA → SILK FFmpeg→PCM→SILK编码 🔄 中等性能
SILK → MP3/OGA/WAV SILK解码→PCM→FFmpeg 🔄 中等性能
Base64 → 任意格式 解码→对应转换路径 📦 灵活处理
-
直接转换流程(高效路径)
- 输入验证 → FFmpeg格式转换 → 质量检查 → 输出
- 适用:WAV/MP3/OGA之间的转换
-
PCM中转流程(兼容路径)
- 输入验证 → 解码到PCM → 编码到目标格式 → 质量检查 → 输出
- 适用:所有涉及SILK格式的转换
docker run -d \
--name silk-converter \
-p 8321:8321 \
--restart unless-stopped \
iptag/audio-converter:latest# 1. 克隆项目
git clone <repository-url>
cd silk-to-mp3-docker
# 2. 构建镜像(多阶段构建,约需5-10分钟)
docker build -t silk-to-mp3-converter .
# 3. 启动服务
docker run -d \
--name silk-converter \
-p 8321:8321 \
--restart unless-stopped \
silk-to-mp3-converter
# 4. 验证服务
curl http://localhost:8321/health# docker-compose.yml
version: '3.8'
services:
silk-converter:
build: .
ports:
- "8321:8321"
restart: unless-stopped
environment:
- PYTHONUNBUFFERED=1
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8321/health"]
interval: 30s
timeout: 10s
retries: 3# 启动服务
docker-compose up -d
# 查看日志
docker-compose logs -f silk-converter| 端点 | 方法 | 功能 | 响应格式 |
|---|---|---|---|
/health |
GET | 健康检查 | JSON |
/convert |
POST | 音频转换 | 文件/JSON |
GET /health
# 响应
{
"status": "ok"
}请求格式
POST /convert
Content-Type: multipart/form-data
# 参数
file: 音频文件 (必需)
format: 目标格式 (可选,默认mp3)基础格式转换示例
# WAV转MP3(返回文件 + 时长响应头)
curl -F "file=@audio.wav" -F "format=mp3" \
http://localhost:8321/convert \
--output converted.mp3 -v
# 查看响应头中的时长信息
# X-Audio-Duration: 18
# MP3转OGA(Telegram语音格式)
curl -F "file=@audio.mp3" -F "format=oga" \
http://localhost:8321/convert \
--output converted.oga -v
# OGA转MP3
curl -F "file=@audio.oga" -F "format=mp3" \
http://localhost:8321/convert \
--output converted.mp3 -vSILK格式转换示例
# SILK转MP3(返回文件)
curl -F "file=@voice.silk" -F "format=mp3" \
http://localhost:8321/convert \
--output converted.mp3 -v
# WAV转SILK(返回JSON)
curl -F "file=@audio.wav" -F "format=silk" \
http://localhost:8321/convert
# 响应示例
{
"base64": "AiMhU0lMS19WMwwApyt096...",
"duration": 18
}请求格式
POST /convert
Content-Type: application/json
{
"base64_data": "SILK格式的Base64编码数据",
"format": "目标格式"
}使用示例
# Base64 SILK数据转MP3
curl -X POST \
-H "Content-Type: application/json" \
-d '{
"base64_data": "AiMhU0lMS19WMwwApyt096...",
"format": "mp3"
}' \
http://localhost:8321/convert \
--output converted.mp3 -v
# Base64 SILK数据转OGA
curl -X POST \
-H "Content-Type: application/json" \
-d '{
"base64_data": "AiMhU0lMS19WMwwApyt096...",
"format": "oga"
}' \
http://localhost:8321/convert \
--output converted.oga -vHTTP/1.1 200 OK
Content-Type: audio/mp3
Content-Disposition: attachment; filename="audio.mp3"
X-Audio-Duration: 18
Content-Length: 284672
[音频文件二进制数据]{
"base64": "AiMhU0lMS19WMwwApyt096Qx8A5wgANABgAE...",
"duration": 18
}字段说明
base64: SILK格式音频的Base64编码数据duration: 音频时长(秒,向上取整)
{
"error": "错误描述信息",
"details": {
"command": "执行的命令",
"stdout": "标准输出",
"stderr": "错误输出"
}
}常见错误码
400: 请求参数错误(文件格式不支持、缺少必需参数等)500: 服务器内部错误(转换失败、文件处理错误等)
┌─────────────────────────────────────────────────────────────┐
│ Docker 多阶段构建 │
│ ┌─────────────────┐ ┌─────────────────────────┐ │
│ │ Builder Stage │ │ Runtime Stage │ │
│ │ │ │ │ │
│ │ • Ubuntu 22.04 │ ────────► │ • Python 3.11-slim │ │
│ │ • 编译工具链 │ │ • 运行时二进制文件 │ │
│ │ • FFmpeg源码编译 │ │ • Flask应用 │ │
│ │ • SILK SDK编译 │ │ • 调试工具 │ │
│ └─────────────────┘ └─────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
silk-to-mp3-docker/
├── 🐳 Dockerfile # 多阶段Docker构建配置
├── 🐍 api_server.py # Flask API服务器主程序
├── ⚙️ custom_encoder.cpp # 优化的SILK编码器实现
├── 📁 silk_decoder/ # SILK解码器模块
│ ├── Makefile # 解码器编译配置
│ ├── interface/ # API头文件
│ │ ├── SKP_Silk_SDK_API.h # 主API接口定义
│ │ ├── SKP_Silk_control.h # 控制结构定义
│ │ ├── SKP_Silk_errors.h # 错误码定义
│ │ └── SKP_Silk_typedef.h # 类型定义
│ ├── src/ # 解码器源码
│ └── test/ # 测试程序
├── 📁 silk_encoder/ # SILK编码器模块
│ ├── Makefile # 编码器编译配置
│ ├── interface/ # API头文件(与decoder相同)
│ ├── src/ # 编码器源码
│ └── test/ # 测试程序
└── 📖 README.md # 项目文档
# 编译配置
./configure \
--prefix=/opt/ffmpeg \
--enable-version3 --enable-gpl --enable-nonfree \
--enable-libopencore-amrnb --enable-libopencore-amrwb \
--enable-libmp3lame --enable-libvorbis \
--enable-static --disable-shared \
--extra-cflags="-static" --extra-ldflags="-static"// 编码器控制结构
typedef struct {
SKP_int32 API_sampleRate; // 8000/12000/16000/24000
SKP_int32 maxInternalSampleRate; // 最大内部采样率
SKP_int packetSize; // 20/40/60/80/100ms包大小
SKP_int32 bitRate; // 目标比特率
SKP_int packetLossPercentage; // 丢包率 (0-100)
SKP_int complexity; // 复杂度 (0-2)
SKP_int useInBandFEC; // 带内前向纠错
SKP_int useDTX; // 不连续传输
} SKP_SILK_SDK_EncControlStruct;- 内存管理: 动态分配编码器状态结构
- 错误处理: 容错机制,警告而非中断
- 格式兼容: 标准SILK v3头部格式
- 性能优化: O3编译优化,静态链接
| 格式 | 编码器 | 采样率 | 比特率 | 声道 | 质量设置 |
|---|---|---|---|---|---|
| MP3 | LAME | 保持原始 | 128kbps | 保持原始 | VBR模式 |
| OGA | Vorbis | 保持原始 | 质量等级8 | 保持原始 | 高质量压缩 |
| SILK | Custom | 24kHz | 24kbps | 单声道 | 语音优化 |
| PCM | - | 24kHz | 16-bit | 单声道 | 中间格式 |
# 基础健康检查
curl -f http://localhost:8321/health
# 详细健康检查(包含响应时间)
time curl -s http://localhost:8321/health | jq .
# 预期响应
{
"status": "ok"
}# 创建测试音频文件(使用FFmpeg)
ffmpeg -f lavfi -i "sine=frequency=1000:duration=5" -ar 44100 test.wav
# WAV转MP3测试
curl -F "file=@test.wav" -F "format=mp3" \
http://localhost:8321/convert \
--output test_converted.mp3 -v
# 验证转换结果
ffprobe test_converted.mp3
# OGA转MP3测试
curl -F "file=@test.oga" -F "format=mp3" \
http://localhost:8321/convert \
--output test_oga_to_mp3.mp3 -v# WAV转SILK测试(返回JSON)
curl -F "file=@test.wav" -F "format=silk" \
http://localhost:8321/convert | jq .
# 预期响应格式
{
"base64": "AiMhU0lMS19WMwwApyt096...",
"duration": 5
}
# SILK转MP3测试(如果有SILK文件)
curl -F "file=@voice.silk" -F "format=mp3" \
http://localhost:8321/convert \
--output silk_to_mp3.mp3 -v# 使用之前转换得到的Base64数据
BASE64_DATA="AiMhU0lMS19WMwwApyt096..."
# Base64转MP3
curl -X POST \
-H "Content-Type: application/json" \
-d "{\"base64_data\": \"$BASE64_DATA\", \"format\": \"mp3\"}" \
http://localhost:8321/convert \
--output base64_to_mp3.mp3 -v
# Base64转OGA
curl -X POST \
-H "Content-Type: application/json" \
-d "{\"base64_data\": \"$BASE64_DATA\", \"format\": \"oga\"}" \
http://localhost:8321/convert \
--output base64_to_oga.oga -v# 并发测试(需要安装apache2-utils)
ab -n 100 -c 10 -p test_data.json -T application/json \
http://localhost:8321/convert
# 大文件测试
curl -F "file=@large_audio.wav" -F "format=mp3" \
http://localhost:8321/convert \
--output large_converted.mp3 -v
# 响应时间测试
time curl -F "file=@test.wav" -F "format=silk" \
http://localhost:8321/convert > /dev/null# 无效文件格式测试
curl -F "file=@invalid.txt" -F "format=mp3" \
http://localhost:8321/convert
# 缺少参数测试
curl -X POST http://localhost:8321/convert
# 无效Base64数据测试
curl -X POST \
-H "Content-Type: application/json" \
-d '{"base64_data": "invalid_base64", "format": "mp3"}' \
http://localhost:8321/convertconst axios = require('axios');
const fs = require('fs');
const FormData = require('form-data');
class SilkConverter {
constructor(baseURL = 'http://localhost:8321') {
this.baseURL = baseURL;
}
// 文件转换
async convertFile(audioPath, format = 'mp3') {
const formData = new FormData();
formData.append('file', fs.createReadStream(audioPath));
formData.append('format', format);
try {
const response = await axios.post(`${this.baseURL}/convert`, formData, {
headers: { ...formData.getHeaders() },
responseType: format === 'silk' ? 'json' : 'stream'
});
// 获取音频时长
const duration = parseInt(response.headers['x-audio-duration'] || '0');
return {
data: response.data,
duration: duration,
format: format
};
} catch (error) {
throw new Error(`转换失败: ${error.response?.data?.error || error.message}`);
}
}
// Base64数据转换
async convertBase64(base64Data, format = 'mp3') {
try {
const response = await axios.post(`${this.baseURL}/convert`, {
base64_data: base64Data,
format: format
}, {
headers: { 'Content-Type': 'application/json' },
responseType: 'stream'
});
const duration = parseInt(response.headers['x-audio-duration'] || '0');
return {
data: response.data,
duration: duration,
format: format
};
} catch (error) {
throw new Error(`转换失败: ${error.response?.data?.error || error.message}`);
}
}
// 健康检查
async healthCheck() {
try {
const response = await axios.get(`${this.baseURL}/health`);
return response.data.status === 'ok';
} catch (error) {
return false;
}
}
}
// 使用示例
async function example() {
const converter = new SilkConverter();
// 检查服务状态
if (!(await converter.healthCheck())) {
console.error('服务不可用');
return;
}
try {
// WAV转SILK
const silkResult = await converter.convertFile('audio.wav', 'silk');
console.log(`SILK转换完成,时长: ${silkResult.duration}秒`);
console.log(`Base64数据: ${silkResult.data.base64.substring(0, 50)}...`);
// SILK Base64转MP3
const mp3Result = await converter.convertBase64(silkResult.data.base64, 'mp3');
console.log(`MP3转换完成,时长: ${mp3Result.duration}秒`);
// 保存MP3文件
mp3Result.data.pipe(fs.createWriteStream('converted.mp3'));
} catch (error) {
console.error('转换失败:', error.message);
}
}
module.exports = SilkConverter;import requests
import base64
from typing import Optional, Union, BinaryIO
class SilkConverter:
def __init__(self, base_url: str = "http://localhost:8321"):
self.base_url = base_url
def health_check(self) -> bool:
"""健康检查"""
try:
response = requests.get(f"{self.base_url}/health")
return response.json().get("status") == "ok"
except:
return False
def convert_file(self, file_path: str, format: str = "mp3") -> dict:
"""文件转换"""
with open(file_path, 'rb') as f:
files = {'file': f}
data = {'format': format}
response = requests.post(f"{self.base_url}/convert",
files=files, data=data)
if response.status_code != 200:
raise Exception(f"转换失败: {response.json().get('error', '未知错误')}")
# SILK格式返回JSON
if format == 'silk':
return response.json()
# 其他格式返回文件数据
duration = int(response.headers.get('X-Audio-Duration', 0))
return {
'data': response.content,
'duration': duration,
'format': format
}
def convert_base64(self, base64_data: str, format: str = "mp3") -> dict:
"""Base64数据转换"""
payload = {
"base64_data": base64_data,
"format": format
}
response = requests.post(f"{self.base_url}/convert", json=payload)
if response.status_code != 200:
raise Exception(f"转换失败: {response.json().get('error', '未知错误')}")
duration = int(response.headers.get('X-Audio-Duration', 0))
return {
'data': response.content,
'duration': duration,
'format': format
}
# 使用示例
if __name__ == "__main__":
converter = SilkConverter()
# 健康检查
if not converter.health_check():
print("服务不可用")
exit(1)
try:
# WAV转SILK
silk_result = converter.convert_file("audio.wav", "silk")
print(f"SILK转换完成,时长: {silk_result['duration']}秒")
# SILK转MP3
mp3_result = converter.convert_base64(silk_result['base64'], "mp3")
print(f"MP3转换完成,时长: {mp3_result['duration']}秒")
# 保存MP3文件
with open("converted.mp3", "wb") as f:
f.write(mp3_result['data'])
except Exception as e:
print(f"转换失败: {e}")# 查看容器状态
docker ps | grep silk-converter
# 查看容器日志
docker logs -f silk-converter
# 查看容器资源使用
docker stats silk-converter
# 进入容器调试
docker exec -it silk-converter bash# 监控API响应时间
while true; do
time curl -s http://localhost:8321/health > /dev/null
sleep 5
done
# 监控转换性能
time curl -F "file=@test.wav" -F "format=mp3" \
http://localhost:8321/convert > /dev/null# 检查FFmpeg版本
docker exec silk-converter ffmpeg -version
# 检查SILK编码器
docker exec silk-converter ls -la /app/silk_encoder/encoder
# 检查Python依赖
docker exec silk-converter pip list
# 查看临时文件目录
docker exec silk-converter ls -la /app/uploads/- 时长信息: 所有时长都是向上取整的整数(秒),使用FFprobe精确计算
- SILK格式: 专为语音优化,固定采样率24kHz,单声道,比特率24kbps
- 文件清理: 转换完成后自动清理临时文件,避免磁盘空间占用
- 响应格式: SILK输出返回JSON格式,其他格式返回文件+时长响应头
- 编码质量: 建议使用高质量源文件以获得最佳转换效果
-
生产环境部署
- 使用Docker Compose进行服务编排
- 配置健康检查和自动重启策略
- 设置适当的资源限制(CPU、内存)
- 配置日志轮转避免日志文件过大
-
性能优化
- 对于高并发场景,考虑使用负载均衡
- 监控转换时间,必要时进行水平扩展
- 定期清理临时文件目录
-
安全考虑
- 在生产环境中添加文件大小限制
- 实施请求频率限制防止滥用
- 考虑添加身份验证机制
| 问题 | 可能原因 | 解决方案 |
|---|---|---|
| 转换失败 | 文件格式不支持 | 检查输入文件格式,确保在支持列表中 |
| SILK编码错误 | PCM文件异常 | 检查FFmpeg转换步骤,验证PCM文件完整性 |
| 服务无响应 | 容器资源不足 | 增加内存限制,检查CPU使用率 |
| 时长信息错误 | FFprobe执行失败 | 检查FFprobe版本和文件权限 |
# 1. 克隆项目
git clone <repository-url>
cd silk-to-mp3-docker
# 2. 本地开发(需要安装FFmpeg和Python依赖)
pip install flask
python api_server.py
# 3. 代码格式化
black api_server.py
flake8 api_server.py
# 4. 测试
python -m pytest tests/- 遵循Conventional Commits规范
- 提供详细的测试用例
- 更新相关文档
MIT License - 详见 LICENSE 文件
- Skype SILK SDK - 提供SILK编解码核心功能
项目维护: 如有问题或建议,请提交Issue或Pull Request