基于大疆上云API,使用喊话器控件,进行含喊话,但上云API不直接支持,需要自己根据喊话器要求的参数进行编码为Opus裸流,写此目的是让其他人遇到该问题的人避坑。
官方文档中,参数如下图
但是根据该参数进行编码,得到的Opus裸流文件,与大疆-司空2上得到的裸流文件,是其大小的0.5倍,且编码之后得到的文件,喊话器不能正常播放声音,且大疆SDK技术论坛上的客户支撑,感觉也很”官方“回复,使用官方提供的这些参数,在实现过程中也发现一些不合理之处,且根据官方参数,自己选用的一个文件进行编码,不可能得到大疆司空2生成的裸流文件那么大,后怀疑官方参数有误,经过尝试,将编码参数16kps改为了32kps编码之后得到的opus裸流文件,就可以正常通过喊话器进行播放了,附带转码C代码(直接gcc编译该c文件,生成可执行文件),然后,将输入的pcm文件,与输出的目标opus文件的绝对地址作为参数即可转码,也存在基于python的实现,但是底层也会调用该opus库,如果有需要可以私发。
#include <stdio.h>#include <string.h>#include <stdlib.h>#include <opus/opus.h>// 需要自行安装opus依赖,可通过apt 命令进行安装#define SAMPLE_RATE 16000#define CHANNELS 1#define BITERATE 32000#define frame_SIZE 40#define MAX_frame_BYTES 160int pcm_opus_raw(const char *pcm_path, const char *opus_path) { // 1. 打开PCM文件(读二进制)和Opus裸流文件(写二进制) FILE *pcm_fp = fopen(pcm_path, "rb"); FILE *opus_fp = fopen(opus_path, "wb"); if (!pcm_fp || !opus_fp) { perror("File open failed"); return -1; } // 2. 初始化Opus编码器 int error; OpusEncoder *encoder = opus_encoder_create(SAMPLE_RATE, CHANNELS, OPUS_APPLICATION_VOIP, &error); if (error != OPUS_OK) { fprintf(stderr, "Encoder create failed: %s\n", opus_strerror(error)); return -1; } // 3. 设置编码器参数 opus_encoder_ctl(encoder, OPUS_SET_BITRATE(BITERATE)); opus_encoder_ctl(encoder, OPUS_SET_EXPERT_frame_DURATION(frame_SIZE)); opus_encoder_ctl(encoder, OPUS_SET_VBR(0)); opus_encoder_ctl(encoder, OPUS_SET_COMPLEXITY(0)); // 4. 计算单帧采样点数和字节数(16bit 有符号整数) const int frame_samples = SAMPLE_RATE * frame_SIZE / 1000; const int frame_bytes = frame_samples * CHANNELS * sizeof(opus_int16); opus_int16 *pcm_frame = (opus_int16 *)malloc(frame_bytes); if (!pcm_frame) { perror("Malloc failed"); return -1; } // 5. 分帧编码主循环 unsigned char opus_buf[MAX_frame_BYTES]; size_t read_bytes; while ((read_bytes = fread(pcm_frame, 1, frame_bytes, pcm_fp)) == frame_bytes) { // 编码 PCM 帧为 Opus 包 int encode_len = opus_encode( encoder, pcm_frame, frame_samples, opus_buf, MAX_frame_BYTES ); if (encode_len < 0) { fprintf(stderr, "Encode failed: %s\n", opus_strerror(encode_len)); break; } // 写入 Opus 裸流 fwrite(opus_buf, 1, encode_len, opus_fp); } // 6. 资源释放 free(pcm_frame); opus_encoder_destroy(encoder); fclose(pcm_fp); fclose(opus_fp); printf("Encode success! Output: %s\n", opus_path); return 0;}int main(int argc, char *argv[]) { if (argc != 3) { fprintf(stderr, "Usage: %s <input.pcm> <output.opus>\n", argv[0]); return -1; } const char *pcm_path = argv[1]; const char *opus_path = argv[2]; return pcm_opus_raw(pcm_path, opus_path);}
