失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > 使用FFMpeg将音频PCM数据生成WAV和MP3文件

使用FFMpeg将音频PCM数据生成WAV和MP3文件

时间:2020-07-08 18:21:19

相关推荐

使用FFMpeg将音频PCM数据生成WAV和MP3文件

文章目录

1. 获取编码器和创建解码器上下文2. 创建音频流和输出封装上下文3. 编码原始数据写入到文件中

WAV音频封装格式可以存储无编码的PCM数据,而MP3封装格式中不能直接存储PCM数据,需要对数据进行编码;使用FFMpeg将原始的PCM数据生成WAV和MP3文件的步骤如下:

创建编码器AVCodec和编码码器上下文AVCodecContext创建音频流AVStream和输出封装上下文AVFormatContext将原始的PCM数据的AVFrame转换为便那后的AVPacket将AVPacket使用封装上下文写入到文件中。

1. 获取编码器和创建解码器上下文

使用函数avcodec_find_encoder()获取编码器AVCodec使用函数avcodec_alloc_context3()创建编码器上下文AVCodecContext为编码器设置参数,音频采样率、采样格式、通道数等信息。打开编码器

整体的头文件定义如下:

#ifndef AUIDO_GENERATER_H#define AUIDO_GENERATER_H#include <QObject>extern "C"{#include "libavformat/avformat.h"#include "libavcodec/avcodec.h"#include "libswresample/swresample.h"}class AudioGenerater : public QObject{Q_OBJECTpublic:enum AudioFormatType{AudioFormat_WAV,AudioFormat_MP3};public:AudioGenerater(QObject *parent = nullptr);~AudioGenerater();// 生成音频文件void generateAudioFile(AudioFormatType formatType, QString fileName, QByteArray pcmData);private:AVCodec *m_AudioCodec = nullptr;AVCodecContext *m_AudioCodecContext = nullptr;AVFormatContext *m_FormatContext = nullptr;AVOutputFormat *m_OutputFormat = nullptr;// init Formatbool initFormat(QString audioFileName);};#endif

获取编码器和创建解码器上下文代码如下:

AVCodecID codecID = AV_CODEC_ID_PCM_S16LE;if (formatType == AudioFormat_MP3)codecID = AV_CODEC_ID_MP3;// 查找Codecm_AudioCodec = avcodec_find_encoder(codecID);if (m_AudioCodec == nullptr)return;// 创建编码器上下文m_AudioCodecContext = avcodec_alloc_context3(m_AudioCodec);if (m_AudioCodecContext == nullptr)return;// 设置参数m_AudioCodecContext->bit_rate = 64000;m_AudioCodecContext->sample_rate = 44100;if (formatType == AudioFormat_WAV)m_AudioCodecContext->sample_fmt = AV_SAMPLE_FMT_S16;elsem_AudioCodecContext->sample_fmt = AV_SAMPLE_FMT_S16P;m_AudioCodecContext->channels = 2;m_AudioCodecContext->channel_layout = av_get_default_channel_layout(2);m_AudioCodecContext->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;// 打开编码器int result = avcodec_open2(m_AudioCodecContext, m_AudioCodec, nullptr);if (result < 0)goto end;

这里对于生成WAV格式的文件采样格式为AV_SAMPLE_FMT_S16, 生成MP3格式的文件采样格式为AV_SAMPLE_FMT_S16P。S16和S16P的区别在于左右声道数据的存储顺序

S16:LRLRLRLRLRLRLRLRLR…

S16P:LLLLLLLL…RRRRRRR…

2. 创建音频流和输出封装上下文

创建音频流和封装上下文的步骤如下:

使用函数avformat_alloc_output_context2()创建Format上下文使用函数**avformat_new_stream()**创建一个流设置流中的参数使用函数avio_open打开文件

示例代码如下:

bool AudioGenerater::initFormat(QString audioFileName){// 创建输出 Format 上下文int result = avformat_alloc_output_context2(&m_FormatContext, nullptr, nullptr, audioFileName.toLocal8Bit().data());if (result < 0)return false;m_OutputFormat = m_FormatContext->oformat;// 创建音频流AVStream *audioStream = avformat_new_stream(m_FormatContext, m_AudioCodec);if (audioStream == nullptr){avformat_free_context(m_FormatContext);return false;}// 设置流中的参数audioStream->id = m_FormatContext->nb_streams - 1;audioStream->time_base = { 1, 44100 };result = avcodec_parameters_from_context(audioStream->codecpar, m_AudioCodecContext);if (result < 0){avformat_free_context(m_FormatContext);return false;}// 打印FormatContext信息av_dump_format(m_FormatContext, 0, audioFileName.toLocal8Bit().data(), 1);// 打开文件IOif (!(m_OutputFormat->flags & AVFMT_NOFILE)){result = avio_open(&m_FormatContext->pb, audioFileName.toLocal8Bit().data(), AVIO_FLAG_WRITE);if (result < 0){avformat_free_context(m_FormatContext);return false;}}return true;}

3. 编码原始数据写入到文件中

代码如下:

// 写入文件头result = avformat_write_header(m_FormatContext, nullptr);if (result < 0)goto end;// 创建FrameAVFrame *frame = av_frame_alloc();if (frame == nullptr)goto end;int nb_samples = 0;if (m_AudioCodecContext->codec->capabilities & AV_CODEC_CAP_VARIABLE_FRAME_SIZE)nb_samples = 10000;elsenb_samples = m_AudioCodecContext->frame_size;// 设置Frame的参数frame->nb_samples = nb_samples;frame->format = m_AudioCodecContext->sample_fmt;frame->channel_layout = m_AudioCodecContext->channel_layout;// 申请数据内存result = av_frame_get_buffer(frame, 0);if (result < 0){av_frame_free(&frame);goto end;}// 设置Frame为可写result = av_frame_make_writable(frame);if (result < 0){av_frame_free(&frame);goto end;}int perFrameDataSize = frame->linesize[0];if (formatType == AudioFormat_MP3)perFrameDataSize *= 2;int count = pcmData.size() / perFrameDataSize;bool needAddOne = false;if (pcmData.size() % perFrameDataSize != 0){count++;needAddOne = true;}int frameCount = 0;for (int i = 0; i < count; ++i){// 创建PacketAVPacket *pkt = av_packet_alloc();if (pkt == nullptr)goto end;av_init_packet(pkt);if (i == count - 1)perFrameDataSize = pcmData.size() % perFrameDataSize;// 设置数据if (formatType == AudioFormat_WAV){// 合成WAV文件memset(frame->data[0], 0, perFrameDataSize);memcpy(frame->data[0], &(pcmData.data()[perFrameDataSize * i]), perFrameDataSize);}else{memset(frame->data[0], 0, frame->linesize[0]);memset(frame->data[1], 0, frame->linesize[0]);#if 1// 合成MP3文件int channelLayout = av_get_default_channel_layout(2);// 格式转换 S16->S16PSwrContext *swrContext = swr_alloc_set_opts(nullptr, channelLayout, AV_SAMPLE_FMT_S16P, 44100, \channelLayout, AV_SAMPLE_FMT_S16, 44100, 0, nullptr);swr_init(swrContext);uchar *pSrcData[1] = {0};pSrcData[0] = (uchar*)&(pcmData.data()[perFrameDataSize * i]);swr_convert(swrContext, frame->data, frame->nb_samples, \(const uint8_t **)pSrcData, frame->nb_samples);swr_free(&swrContext);AVRational rational;rational.den = m_AudioCodecContext->sample_rate;rational.num = 1;//frame->pts = av_rescale_q(0, rational, m_AudioCodecContext->time_base);#endif}frame->pts = frameCount++;// 发送Frameresult = avcodec_send_frame(m_AudioCodecContext, frame);if (result < 0)continue;// 接收编码后的Packetresult = avcodec_receive_packet(m_AudioCodecContext, pkt);if (result < 0){av_packet_free(&pkt);continue;}// 写入文件av_packet_rescale_ts(pkt, m_AudioCodecContext->time_base, m_FormatContext->streams[0]->time_base);pkt->stream_index = 0;result = av_interleaved_write_frame(m_FormatContext, pkt);if (result < 0)continue;av_packet_free(&pkt);}// 写入文件尾av_write_trailer(m_FormatContext);// 关闭文件IOavio_closep(&m_FormatContext->pb);// 释放Frame内存av_frame_free(&frame);end:avcodec_free_context(&m_AudioCodecContext);if (m_FormatContext != nullptr)avformat_free_context(m_FormatContext);

函数avformat_write_header()av_write_trailer()表示写入文件头和文件尾,写入音频数据前写入头,完成音频写入后,写入文件尾。函数av_frame_alloc()创建AVFrame数据,函数av_frame_free()释放AVFrame数据。函数av_frame_get_buffer()为AVFrame申请数据的存储空间。其中AVFrame中的成员linesize存储每个通道的数据字节大小,data中存储数据对于planes类型的数据每个通道是分别存储的。函数av_packet_alloc()为AVPacket分配内存,函数av_packet_free()为AVPacket释放内存。函数avcodec_send_frame()发送原始的AVFrame,函数avcodec_receive_packet()接收编码后的AVPacket。函数av_interleaved_write_frame()将编码后的AVPacket写入到文件中。函数avio_closep()关闭文件。

完整代码如下:

.CPP文件

#include "AudioGenerater.h"#include <QDebug>AudioGenerater::AudioGenerater(QObject *parent):QObject(parent){av_register_all();avcodec_register_all();}AudioGenerater::~AudioGenerater(){}void AudioGenerater::generateAudioFile(AudioFormatType formatType, QString fileName, QByteArray pcmData){AVCodecID codecID = AV_CODEC_ID_PCM_S16LE;if (formatType == AudioFormat_MP3)codecID = AV_CODEC_ID_MP3;// 查找Codecm_AudioCodec = avcodec_find_encoder(codecID);if (m_AudioCodec == nullptr)return;// 创建编码器上下文m_AudioCodecContext = avcodec_alloc_context3(m_AudioCodec);if (m_AudioCodecContext == nullptr)return;// 设置参数m_AudioCodecContext->bit_rate = 64000;m_AudioCodecContext->sample_rate = 44100;if (formatType == AudioFormat_WAV)m_AudioCodecContext->sample_fmt = AV_SAMPLE_FMT_S16;elsem_AudioCodecContext->sample_fmt = AV_SAMPLE_FMT_S16P;m_AudioCodecContext->channels = 2;m_AudioCodecContext->channel_layout = av_get_default_channel_layout(2);m_AudioCodecContext->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;// 打开编码器int result = avcodec_open2(m_AudioCodecContext, m_AudioCodec, nullptr);if (result < 0)goto end;// 创建封装if (!initFormat(fileName))goto end;// 写入文件头result = avformat_write_header(m_FormatContext, nullptr);if (result < 0)goto end;// 创建FrameAVFrame *frame = av_frame_alloc();if (frame == nullptr)goto end;int nb_samples = 0;if (m_AudioCodecContext->codec->capabilities & AV_CODEC_CAP_VARIABLE_FRAME_SIZE)nb_samples = 10000;elsenb_samples = m_AudioCodecContext->frame_size;// 设置Frame的参数frame->nb_samples = nb_samples;frame->format = m_AudioCodecContext->sample_fmt;frame->channel_layout = m_AudioCodecContext->channel_layout;// 申请数据内存result = av_frame_get_buffer(frame, 0);if (result < 0){av_frame_free(&frame);goto end;}// 设置Frame为可写result = av_frame_make_writable(frame);if (result < 0){av_frame_free(&frame);goto end;}int perFrameDataSize = frame->linesize[0];if (formatType == AudioFormat_MP3)perFrameDataSize *= 2;int count = pcmData.size() / perFrameDataSize;bool needAddOne = false;if (pcmData.size() % perFrameDataSize != 0){count++;needAddOne = true;}int frameCount = 0;for (int i = 0; i < count; ++i){// 创建PacketAVPacket *pkt = av_packet_alloc();if (pkt == nullptr)goto end;av_init_packet(pkt);if (i == count - 1)perFrameDataSize = pcmData.size() % perFrameDataSize;// 设置数据if (formatType == AudioFormat_WAV){// 合成WAV文件memset(frame->data[0], 0, perFrameDataSize);memcpy(frame->data[0], &(pcmData.data()[perFrameDataSize * i]), perFrameDataSize);}else{memset(frame->data[0], 0, frame->linesize[0]);memset(frame->data[1], 0, frame->linesize[0]);#if 1// 合成MP3文件int channelLayout = av_get_default_channel_layout(2);// 格式转换 S16->S16PSwrContext *swrContext = swr_alloc_set_opts(nullptr, channelLayout, AV_SAMPLE_FMT_S16P, 44100, \channelLayout, AV_SAMPLE_FMT_S16, 44100, 0, nullptr);swr_init(swrContext);uchar *pSrcData[1] = {0};pSrcData[0] = (uchar*)&(pcmData.data()[perFrameDataSize * i]);swr_convert(swrContext, frame->data, frame->nb_samples, \(const uint8_t **)pSrcData, frame->nb_samples);swr_free(&swrContext);AVRational rational;rational.den = m_AudioCodecContext->sample_rate;rational.num = 1;//frame->pts = av_rescale_q(0, rational, m_AudioCodecContext->time_base);#endif}frame->pts = frameCount++;// 发送Frameresult = avcodec_send_frame(m_AudioCodecContext, frame);if (result < 0)continue;// 接收编码后的Packetresult = avcodec_receive_packet(m_AudioCodecContext, pkt);if (result < 0){av_packet_free(&pkt);continue;}// 写入文件av_packet_rescale_ts(pkt, m_AudioCodecContext->time_base, m_FormatContext->streams[0]->time_base);pkt->stream_index = 0;result = av_interleaved_write_frame(m_FormatContext, pkt);if (result < 0)continue;av_packet_free(&pkt);}// 写入文件尾av_write_trailer(m_FormatContext);// 关闭文件IOavio_closep(&m_FormatContext->pb);// 释放Frame内存av_frame_free(&frame);end:avcodec_free_context(&m_AudioCodecContext);if (m_FormatContext != nullptr)avformat_free_context(m_FormatContext);}bool AudioGenerater::initFormat(QString audioFileName){// 创建输出 Format 上下文int result = avformat_alloc_output_context2(&m_FormatContext, nullptr, nullptr, audioFileName.toLocal8Bit().data());if (result < 0)return false;m_OutputFormat = m_FormatContext->oformat;// 创建音频流AVStream *audioStream = avformat_new_stream(m_FormatContext, m_AudioCodec);if (audioStream == nullptr){avformat_free_context(m_FormatContext);return false;}// 设置流中的参数audioStream->id = m_FormatContext->nb_streams - 1;audioStream->time_base = { 1, 44100 };result = avcodec_parameters_from_context(audioStream->codecpar, m_AudioCodecContext);if (result < 0){avformat_free_context(m_FormatContext);return false;}// 打印FormatContext信息av_dump_format(m_FormatContext, 0, audioFileName.toLocal8Bit().data(), 1);// 打开文件IOif (!(m_OutputFormat->flags & AVFMT_NOFILE)){result = avio_open(&m_FormatContext->pb, audioFileName.toLocal8Bit().data(), AVIO_FLAG_WRITE);if (result < 0){avformat_free_context(m_FormatContext);return false;}}return true;}

如果觉得《使用FFMpeg将音频PCM数据生成WAV和MP3文件》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。