基本思想:不太会ffmpeg解析pcm的音频文件,所以记录一下结合具体的场景和具体的应用
具体生成的pcm使用的官方代码,不详细叙述,官方的唤醒模块将会产生一个pcm文件,可使用下列命令或者代码将pcm转成mp3文件,将进行后续处理,具体业务保密,不累述;
图片来自轮趣科技:其语音主控板主要是 R818 降噪板,其内部集成科大讯飞语音算法,利用麦克风阵列的空域滤波特性,通过唤醒人的角度定位,形成定向拾音波束,并对
波束以外的噪声进行抑制, 提升远场拾音质量。附测试的pcm文件
链接: https://pan.baidu.com/s/1zL7k3w6JLWN9m4vp7B597A?pwd=rre7 提取码: rre7
命令转pcm到mp3
ubuntu@ubuntu:~$ ffmpeg -y -ac 1 -ar 16000 -f s16le -i /home/ubuntu/audio_xufei_arm/cmake-build-debug/vvui_deno.pcm -c:a libmp3lame -q:a 2 output.mp3
ffmpeg version 4.2.7-0ubuntu0.1 Copyright (c) 2000-2022 the FFmpeg developersbuilt with gcc 9 (Ubuntu 9.4.0-1ubuntu1~20.04.1)configuration: --prefix=/usr --extra-version=0ubuntu0.1 --toolchain=hardened --libdir=/usr/lib/x86_64-linux-gnu --incdir=/usr/include/x86_64-linux-gnu --arch=amd64 --enable-gpl --disable-stripping --enable-avresample --disable-filter=resample --enable-avisynth --enable-gnutls --enable-ladspa --enable-libaom --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libcdio --enable-libcodec2 --enable-libflite --enable-libfontconfig --enable-libfreetype --enable-libfribidi --enable-libgme --enable-libgsm --enable-libjack --enable-libmp3lame --enable-libmysofa --enable-libopenjpeg --enable-libopenmpt --enable-libopus --enable-libpulse --enable-librsvg --enable-librubberband --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libspeex --enable-libssh --enable-libtheora --enable-libtwolame --enable-libvidstab --enable-libvorbis --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx265 --enable-libxml2 --enable-libxvid --enable-libzmq --enable-libzvbi --enable-lv2 --enable-omx --enable-openal --enable-opencl --enable-opengl --enable-sdl2 --enable-libdc1394 --enable-libdrm --enable-libiec61883 --enable-nvenc --enable-chromaprint --enable-frei0r --enable-libx264 --enable-sharedlibavutil 56. 31.100 / 56. 31.100libavcodec 58. 54.100 / 58. 54.100libavformat 58. 29.100 / 58. 29.100libavdevice 58. 8.100 / 58. 8.100libavfilter 7. 57.100 / 7. 57.100libavresample 4. 0. 0 / 4. 0. 0libswscale 5. 5.100 / 5. 5.100libswresample 3. 5.100 / 3. 5.100libpostproc 55. 5.100 / 55. 5.100
[s16le @ 0x5648e442e740] Estimating duration from bitrate, this may be inaccurate
Guessed Channel Layout for Input Stream #0.0 : mono
Input #0, s16le, from '/home/ubuntu/audio_xufei_arm/cmake-build-debug/vvui_deno.pcm':Duration: 00:00:04.77, bitrate: 256 kb/sStream #0:0: Audio: pcm_s16le, 16000 Hz, mono, s16, 256 kb/s
Stream mapping:Stream #0:0 -> #0:0 (pcm_s16le (native) -> mp3 (libmp3lame))
Press [q] to stop, [?] for help
Output #0, mp3, to 'output.mp3':Metadata:TSSE : Lavf58.29.100Stream #0:0: Audio: mp3 (libmp3lame), 16000 Hz, mono, s16pMetadata:encoder : Lavc58.54.100 libmp3lame
size= 26kB time=00:00:04.79 bitrate= 44.3kbits/s speed= 424x
video:0kB audio:26kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.856164%
cmakelists.txt
cmake_minimum_required(VERSION 3.16)
project(untitled2)set(CMAKE_CXX_STANDARD 14)add_executable(untitled2 main.cpp)
target_link_libraries(untitled2 -lswresample -lavformat -lavcodec -lswscale -lavutil -lz)
main.cpp 代码修改为采样率为16000 声音为mono 读取字节为512 调整采样为单倍,注意集成项目中需要*.c文件,因为c无法找到cpp文件,或者用extern包裹一下
#include <iostream>#ifdef __cplusplus
extern "C"
{
#endif
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavutil/imgutils.h"
#include "libavutil/log.h"
#include "libswresample/swresample.h"#include "libavutil/avutil.h"
#include "libavutil/opt.h"
#ifdef __cplusplus
}
#endif
using namespace std;#define CHANNEL 1
#define OSR 16000/*** pcm 转 mp3格式,输入文件路径*/
int pcm_to_mp3(const char *pcm_file_path, const char *mp3_file_path)
{FILE *pcm_file = NULL;FILE *mp3_file = NULL;int result;// 获取mp3编码器cout << "获取mp3编码器" << endl;const AVCodec *avCodec = avcodec_find_encoder(AV_CODEC_ID_MP3);if (!avCodec){cout << "初始化mp3 编码器失败" << endl;return -1;}// 创建编码器上下文AVCodecContext *avCodecContext = avcodec_alloc_context3(avCodec);if (!avCodecContext){cout << "avcodec_alloc_context3 失败" << avCodecContext << endl;return -1;}avCodecContext->bit_rate = 64000;avCodecContext->channels = CHANNEL;avCodecContext->channel_layout = AV_CH_LAYOUT_MONO;avCodecContext->sample_rate = OSR;avCodecContext->sample_fmt = AV_SAMPLE_FMT_S16P;avCodecContext->time_base = av_get_time_base_q();// 打开编码器cout << "打开mp3编码器" << endl;result = avcodec_open2(avCodecContext, avCodec, NULL);if (result < 0){cout << "avcodec_open2失败: " << result << endl;return result;}cout << "打开mp3文件" << mp3_file_path << endl;// 打开输出文件mp3_file = fopen(mp3_file_path, "wb");if (!mp3_file){cout << "打开mp3文件失败" << endl;return -1;}// AVFrame 接受重采样的每一帧的音频数据 每帧的样本大小为1152AVFrame *avFrame = av_frame_alloc();if (!avFrame){cout << "分配avFrame帧失败" << endl;return -1;}// mp3一帧的样本数为1152avFrame->nb_samples = 256;avFrame->channels = CHANNEL;avFrame->channel_layout = AV_CH_LAYOUT_MONO;avFrame->format = AV_SAMPLE_FMT_S16P;// 给帧分配内存空间result = av_frame_get_buffer(avFrame, 0);if (result < 0){cout << "分配帧内存失败" << endl;return result;}// 重采样 创建音频重采样上下文cout << "配置重采样器上下文" << endl;SwrContext *swrContext = swr_alloc();if (!swrContext){cout << "配置重采样上下文失败" << endl;return -1;}// 设置重采样输入pcm参数:通道布局:立体声 采样率:44100 样本格式 s16交错存储av_opt_set_int(swrContext, "in_channel_layout", AV_CH_LAYOUT_MONO, 0);av_opt_set_int(swrContext, "in_sample_rate", OSR, 0);av_opt_set_sample_fmt(swrContext, "in_sample_fmt", AV_SAMPLE_FMT_S16, 0);// 设置重采样输出mp3参数:通道布局:立体声 采样率:44100 样本格式 s16平面存储av_opt_set_int(swrContext, "out_channel_layout", AV_CH_LAYOUT_MONO, 0);av_opt_set_int(swrContext, "out_sample_rate", OSR, 0);av_opt_set_sample_fmt(swrContext, "out_sample_fmt", AV_SAMPLE_FMT_S16P, 0);// 重采样初始化result = swr_init(swrContext);if (result < 0){cout << "重采样器初始化失败,error=" << result << endl;return result;}uint8_t **input_data = NULL;uint8_t **output_data = NULL;int input_linesize, output_linesize;// 打开pcm文件cout << "打开源 pcm 文件 " << pcm_file_path << endl;pcm_file = fopen(pcm_file_path, "rb");if (!pcm_file){cout << "打开 pcm 文件失败" << endl;return -1;}cout << "开始编码转换" << endl;// 给pcm文件数据分配空间result = av_samples_alloc_array_and_samples(&input_data, &input_linesize, 2, avFrame->nb_samples, AV_SAMPLE_FMT_S16, 0);if (result < 0){cout << "给pcm文件分配空间失败, result = " << result << endl;return result;}// 缓存重采样数据的空间分配result = av_samples_alloc_array_and_samples(&output_data, &output_linesize, 2, avFrame->nb_samples, AV_SAMPLE_FMT_S16P, 0);if (result < 0){cout << "获取mp3 重采样数据失败, result=" << result << endl;return result;}// 存放编码后的数据AVPacket *avPacket = av_packet_alloc();if (!avPacket){cout << "分配 avPacket 内存失败" << endl;return -1;}cout << "==========循环读入帧==========" << endl;long total_size = 0;while (!feof(pcm_file)){long read_size = (long)fread(input_data[0], 1, avFrame->nb_samples * 2, pcm_file);total_size += read_size;if ((total_size / read_size) % 50 == 0){cout << "读取数据:" << read_size << "字节; 累计:" << total_size << " 字节 " << endl;}if (read_size <= 0){break;}// 重采样result = swr_convert(swrContext, output_data, avFrame->nb_samples, (const uint8_t **)input_data, avFrame->nb_samples);if (result < 0){cout << "音频编码失败,错误信息" << result << endl;return result;}// 将重采样后的数据存入frame,MP3是s16p 先存放左声道的数据 后存放右声道的数据, data[0]是左声道,1是右声道avFrame->data[0] = output_data[0];avFrame->data[1] = output_data[1];// 编码,写入mp3文件,实际上是对frame这个结构体里面的数据进行编码操作,发送到编码线程:使用编码器 和 存储数据的frameresult = avcodec_send_frame(avCodecContext, avFrame);if (result < 0){cout << "mp3编码失败,错误信息:" << result << endl;return result;}while (result >= 0){// 接收编码后的数据,使用编码器 和 存储编码数据的pkt, 有可能需要多次才能接收完成result = avcodec_receive_packet(avCodecContext, avPacket);// AVERROR_EOF表示没有数据了 这两个错误不影响继续接收数据if (result == AVERROR_EOF || result == AVERROR(EAGAIN)){continue;}else if (result < 0){break;}fwrite(avPacket->data, 1, avPacket->size, mp3_file);av_packet_unref(avPacket);}}// 告诉解码器没有帧了,如果没有这几行的逻辑,在关闭 avCodecContext 可能会提示 * fames left in the queu on closingavcodec_send_frame(avCodecContext, __null);while(avcodec_receive_packet(avCodecContext, avPacket)!=AVERROR_EOF);// 关闭缓存if (input_data){av_free(input_data);}if (output_data){av_free(output_data);}cout << "关闭文件" << endl;fclose(pcm_file);fclose(mp3_file);cout << "释放资源" << endl;// s释放 frame pktav_frame_free(&avFrame);av_packet_free(&avPacket);// 释放重采样上下文swr_free(&swrContext);// 释放编码器上下文avcodec_free_context(&avCodecContext);cout << "转码完成" << endl;return 0;
}int main(int argc, char *argv[])
{const char *input = "vvui_deno.pcm";const char *output = "vvui_deno.mp3";pcm_to_mp3(input, output);return 0;
}
参考:
音视频开发9. 使用ffmpeg 将pcm转码mp3实践(C++)_编程圈子的博客-CSDN博客_ffmpeg pcm转mp3