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

5 回复

可以参考三方库中的opus部分,整个项目的readme也可以关注
https://gitcode.com/openharmony-sig/tpc_c_cplusplus/tree/master/thirdparty/opus

路径在opus–>HPKBUILD

cke_291.png

更多关于HarmonyOS 鸿蒙Next中固定的码率想要收到固定40字节的opus数据的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


逐帧解码Opus为PCM。将Opus文件逐帧解码为PCM数据,主要步骤如下:

  1. 准备阶段:打开输入文件,查找流信息,初始化解码器上下文。
  2. 解码循环:持续读取数据包,发送到解码器并接收解码后的帧。
  3. 处理PCM:获取解码后的PCM数据供后续使用。
  4. 刷新解码器:处理完所有数据包后,获取解码器中缓存的帧。
  5. 清理资源:释放所有分配的资源。
// 初始化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,主要步骤如下:

  1. 准备阶段:初始化编码器上下文,并设置参数(如采样率、声道数)。
  2. 编码循环:将PCM帧送入编码器,接收编码后的数据包。
  3. 处理Opus包:获取编码后的Opus数据包。
  4. 刷新编码器:处理完所有帧后,获取编码器中缓存的剩余数据包。
  5. 清理资源:释放所有分配的资源。
// 初始化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编码时,需特别注意以下几点:

  1. 采样率约束:Opus编码支持的输入采样率有限制,必须是8000、12000、16000、24000或48000Hz。
  2. 帧长约束:Opus编码需要按特定的帧长度(如2.5, 5, 10, 20, 40, 60ms)调用编码函数。例如,在48kHz采样率下,合法的单通道帧大小(frame_size)只能是120, 240, 480, 960, 1920, 2880个采样点。 在AVCodecContext中设置frame_size需符合这些要求。
  3. 容器格式: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),输出帧大小仍会根据音频内容(静音/活跃)动态变化,这是正常现象。要获得接近固定大小的帧,可以尝试以下方法:

  1. 启用码率控制约束:在编码器配置中,除了设置目标码率(如16kbps对应20ms帧约40字节),可尝试启用OH_AVKey.OH_Key_Complexity参数并设置为较低值(如0或1),以限制编码器对复杂度的自适应,使输出更稳定。

  2. 强制使用CBR模式:检查编码器是否支持严格CBR模式。部分Opus实现可通过设置OH_AVKey.OH_Key_Opus_Constant_Bitratetrue来强制恒定码率,但需确认鸿蒙的AVCodec是否支持此扩展参数。

  3. 后处理填充:若编码后数据小于40字节,可在帧尾添加静音填充数据(如零字节)至固定长度。但需注意,解码端需识别并去除填充,或使用带帧长的封装格式。

  4. 使用原生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字节可进行填充。

若需更严格的帧长控制,建议评估使用硬件厂商提供的编码库或自定义轻量级编码方案。

回到顶部