HarmonyOS 鸿蒙Next中固定的码率想要收到固定40字节的opus数据
HarmonyOS 鸿蒙Next中固定的码率想要收到固定40字节的opus数据 关于opus编码,我们目前在对接我们的硬件这一块,目前音频是16000采样率16位位深的数据,硬件端是20ms一包编码数据
我们使用鸿蒙的音频编码模块,在调用OH_AudioCodec_PushInputBuffer前,音频的size都 固定为640,但是从OH_AVCodecOnNewOutputBuffer回调函数拿取编码后音频的size不是固定的值而是在8-11左右。
后面发现固定长度的PCM数据编码成opus,编码出来的opus每帧长度大小不同是正常现象。根据提供的PCM音频数据,编码为opus帧时,在编码没有声音的PCM数据时,每个opus编码帧长度就是在8-11字节。在编码有声音的PCM数据时,每个opus编码帧长度在30-50字节。编码后的opus数据不能直接用于解码和播放,需要通过ogg封装才能正常播放,或者在编码时记录opus数据每一帧的长度,在解码时将opus数据按帧送入解码器才能正常解码为PCM数据。
目前三方库FFmpeg,只能将音频编码后写入文件中,但是我们希望能流式的直接获取到编码后opus音频而不是写入文件,好像没有看到有相关的方法。FFmpeg无法流式直接获取编码后音频
主要的问题在于我们编码设置了固定的码率想要收到固定40字节的opus数据而不是动态长度的,有什么办法可以解决
能否提供到一个FFmpeg库实现opus逐帧编码的demo给我们呢,参考的三方库是没有这部分的
我们目前遇到了这个困难,卡在这里了,我们需要对实时流进行编码送给我们已有设备上,有没有一些库推荐,或者demo参考
更多关于HarmonyOS 鸿蒙Next中固定的码率想要收到固定40字节的opus数据的实战教程也可以访问 https://www.itying.com/category-93-b0.html
可以参考三方库中的opus部分,整个项目的readme也可以关注
https://gitcode.com/openharmony-sig/tpc_c_cplusplus/tree/master/thirdparty/opus
路径在opus–>HPKBUILD

更多关于HarmonyOS 鸿蒙Next中固定的码率想要收到固定40字节的opus数据的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
逐帧解码Opus为PCM。将Opus文件逐帧解码为PCM数据,主要步骤如下:
- 准备阶段:打开输入文件,查找流信息,初始化解码器上下文。
- 解码循环:持续读取数据包,发送到解码器并接收解码后的帧。
- 处理PCM:获取解码后的PCM数据供后续使用。
- 刷新解码器:处理完所有数据包后,获取解码器中缓存的帧。
- 清理资源:释放所有分配的资源。
// 初始化Packet和Frame
AVPacket *packet = av_packet_alloc();
AVFrame *frame = av_frame_alloc();
// 解码循环:读取数据包
while (av_read_frame(format_ctx, packet) >= 0) {
if (packet->stream_index == audio_stream_index) {
// 将压缩数据包发送到解码器
if (avcodec_send_packet(codec_ctx, packet) == 0) {
// 尝试从解码器接收解码后的帧
while (avcodec_receive_frame(codec_ctx, frame) == 0) {
// 成功解码一帧,PCM数据在frame->data中
// 此处可以处理PCM数据,例如写入文件或播放
process_decoded_frame(frame);
}
}
}
av_packet_unref(packet); // 释放数据包
}
// 刷新解码器:发送NULL packet以刷新内部缓冲
avcodec_send_packet(codec_ctx, NULL);
while (avcodec_receive_frame(codec_ctx, frame) == 0) {
process_decoded_frame(frame); // 处理剩余的解码帧
}
// 释放资源
av_frame_free(&frame);
av_packet_free(&packet);
逐帧编码PCM为Opus。将PCM数据逐帧编码为Opus,主要步骤如下:
- 准备阶段:初始化编码器上下文,并设置参数(如采样率、声道数)。
- 编码循环:将PCM帧送入编码器,接收编码后的数据包。
- 处理Opus包:获取编码后的Opus数据包。
- 刷新编码器:处理完所有帧后,获取编码器中缓存的剩余数据包。
- 清理资源:释放所有分配的资源。
// 初始化Frame和Packet
AVFrame *frame = av_frame_alloc();
AVPacket *packet = av_packet_alloc();
// 设置帧参数(根据PCM源设置)
frame->sample_rate = sample_rate;
frame->channels = channels;
// ... 其他帧参数 ...
// 编码循环:填充并发送帧
while (get_next_pcm_frame(frame)) { // 假设此函数获取下一帧PCM
// 将原始音频帧发送到编码器
if (avcodec_send_frame(codec_ctx, frame) == 0) {
// 尝试从编码器接收编码后的包
while (avcodec_receive_packet(codec_ctx, packet) == 0) {
// 成功编码一个数据包,压缩数据在packet->data中
// 此处可以处理Opus包,例如写入文件或发送
process_encoded_packet(packet);
av_packet_unref(packet); // 处理完后释放包
}
}
}
// 刷新编码器:发送NULL frame以刷新内部缓冲
avcodec_send_frame(codec_ctx, NULL);
while (avcodec_receive_packet(codec_ctx, packet) == 0) {
process_encoded_packet(packet); // 处理剩余的编码包
av_packet_unref(packet);
}
// 释放资源
av_frame_free(&frame);
av_packet_free(&packet);
使用Opus编码时,需特别注意以下几点:
- 采样率约束:Opus编码支持的输入采样率有限制,必须是8000、12000、16000、24000或48000Hz。
- 帧长约束:Opus编码需要按特定的帧长度(如2.5, 5, 10, 20, 40, 60ms)调用编码函数。例如,在48kHz采样率下,合法的单通道帧大小(frame_size)只能是120, 240, 480, 960, 1920, 2880个采样点。 在AVCodecContext中设置frame_size需符合这些要求。
- 容器格式:Opus流常用的容器格式有Ogg、TS、MKV等。其中Ogg格式兼容性较好。若需要将编码后的Opus包写入文件,通常使用Ogg封装。
在HarmonyOS Next中,要接收固定40字节的Opus数据,需使用AudioCapturer进行音频采集。设置音频参数时,将码率固定,并确保帧大小对应40字节。通过capturer.read(buffer, isBlocking)读取数据,检查buffer长度是否为40字节。若不符,需调整音频参数或处理逻辑。
在HarmonyOS Next中,要实现固定码率下获取固定40字节的opus数据帧,需要理解Opus编码的特性并调整配置。
Opus编码本身是变长编码,即使设置固定码率(如通过OH_AVCodec_SetParameter设置OH_AVKey.OH_Key_Bit_Rate),输出帧大小仍会根据音频内容(静音/活跃)动态变化,这是正常现象。要获得接近固定大小的帧,可以尝试以下方法:
-
启用码率控制约束:在编码器配置中,除了设置目标码率(如16kbps对应20ms帧约40字节),可尝试启用
OH_AVKey.OH_Key_Complexity参数并设置为较低值(如0或1),以限制编码器对复杂度的自适应,使输出更稳定。 -
强制使用CBR模式:检查编码器是否支持严格CBR模式。部分Opus实现可通过设置
OH_AVKey.OH_Key_Opus_Constant_Bitrate为true来强制恒定码率,但需确认鸿蒙的AVCodec是否支持此扩展参数。 -
后处理填充:若编码后数据小于40字节,可在帧尾添加静音填充数据(如零字节)至固定长度。但需注意,解码端需识别并去除填充,或使用带帧长的封装格式。
-
使用原生Opus库:若鸿蒙AVCodec无法满足需求,可集成原生Opus库(如libopus)进行编码。通过
opus_encode()函数编码时,设置OPUS_SET_BITRATE为目标码率,并使用OPUS_APPLICATION_RESTRICTED_LOWDELAY模式可能获得更稳定的帧大小。
关于FFmpeg流式编码:FFmpeg可通过avcodec_send_frame()和avcodec_receive_packet()实现逐帧编码,无需写入文件。但需注意,FFmpeg的Opus编码默认输出可能仍为变长帧。
由于无法直接提供完整demo,建议参考以下关键步骤:
- 初始化编码器时,设置
codec_context->bit_rate为目标码率(如16000)。 - 循环调用
avcodec_send_frame()送入PCM帧,并通过avcodec_receive_packet()获取编码后的Opus包。 - 检查包大小,若未达40字节可进行填充。
若需更严格的帧长控制,建议评估使用硬件厂商提供的编码库或自定义轻量级编码方案。

