使用 FFmpeg 把 PCM 转成 AAC

本文介绍了如何使用FFmpeg 3.4版本编译库和头文件,然后通过示例代码Pcm2Aac.h、Pcm2Aac.cpp和main.cpp将PCM音频转换为AAC格式。在完成编译并运行程序后,成功生成了AAC文件,具有特定的帧率和时长,可以正常播放。提供了一个PCM样例文件的下载链接,并引用了相关教程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ffmpeg-3.4编译出lib库和头文件
配置文件可以是这样 config.sh
主要是 --enable-encoders --enable-swresample

#!/bin/bash

export PREFIX=./../ffmpeg

./configure \
    --disable-yasm \
    --disable-ffplay \
    --disable-ffprobe \
    --disable-ffserver \
    --disable-debug \
    --disable-zlib \
    --disable-bzlib \
    --disable-static \
    --disable-stripping \
    --enable-ffmpeg \
    --enable-shared \
    --enable-gpl \
    --enable-small \
    --target-os=linux \
    --arch=arm \
    --enable-cross-compile \
    --cross-prefix=arm-linux- \
    --cc=arm-linux-gcc \
    --prefix=$PREFIX \
    --enable-encoders \
    --enable-decoders \
    --enable-muxers \
    --enable-demuxers \
    --enable-parsers \
    --enable-bsfs \
    --enable-protocols \
    --disable-filters \
    --disable-avfilter \
    --disable-swscale \
    --enable-swresample \
    --disable-devices \
    --disable-postproc \
    --enable-network \
    --enable-indev=v4l2

Pcm2Aac.h 仅供参考,详见注释

#ifdef __cplusplus
extern "C"
{
#endif
    #include "libavcodec/avcodec.h"
    #include "libavformat/avformat.h"
    #include "libswresample/swresample.h"
#ifdef __cplusplus
};
#endif

class Pcm2AAC
{
public:
    Pcm2AAC();
    ~Pcm2AAC();

    bool Init(int i_nPcmSampleRate, AVSampleFormat i_ePcmSampleFmt, int i_nPcmChannels);
    void AddData(char *i_pData, int i_nSize);
    void FlushData(void);
    bool GetData(char *&o_pData, int &o_nSize);

private:
    void AddADTS(int i_nPktLen);

    int m_nPcmSize;
    int m_nPcmChannel;
    int m_nPcmSampleRate;
    AVSampleFormat m_ePcmFormat;
    char *m_aPcmPointer[AV_NUM_DATA_POINTERS];
    char *m_pPcmData;
    char *m_pOutData;
    AVPacket *m_pPacket;
    AVFrame *m_pFrame;
    AVCodec *m_pCodec;
    AVCodecContext *m_pCodecCtx;
    SwrContext *m_pSwrCtx;
    int64_t m_s64Pts;
    int m_nFrameSize;
    int m_nFrameCnt;
};

Pcm2Aac.cpp 仅供参考,详见注释

#include "Pcm2AAC.h"

// refer to ffmpeg/libavformat/adtsenc.c
#define ADTS_HEADER_SIZE 7
#define ADTS_MAX_FRAME_BYTES ((1 << 13) - 1) // 8K

Pcm2AAC::Pcm2AAC()
{
    m_nPcmSize = 0;
    m_pPacket = NULL;
    m_pFrame = NULL;
    m_pCodec = NULL;
    m_pCodecCtx = NULL;
    m_pSwrCtx = NULL;
    m_s64Pts = 0;
    m_nFrameSize = 0;
    m_nFrameCnt = 0;

    for(int i = 0; i < AV_NUM_DATA_POINTERS; i++)
    {
        m_aPcmPointer[i] = new char[1024 * 10];
    }
    m_pPcmData = new char[1024 * 10];
    m_pOutData = new char[1024 * 10];
    memset(m_pPcmData, 0, 1024 * 10);
    memset(m_pOutData, 0, 1024 * 10);
}

Pcm2AAC::~Pcm2AAC()
{
    // just for test
    // 1秒钟处理了多少帧
    int nFramesPerSecond = m_nPcmSampleRate / m_nFrameSize; // 44100/1024=43
    printf("nFramesPerSecond = %d\n", nFramesPerSecond);
    // 1帧需要多少时间(毫秒)
    double fFrameTime = 1000.00 / nFramesPerSecond; // 1000/43=23.26ms
    printf("fFrameTime = %.2fms\n", fFrameTime);
    // 时间总长 = 总帧数 * 1帧所需时间
    float fDuration = m_nFrameCnt * fFrameTime / 1000; // 2628*23.25=61.12s
    printf("This AAC total frames = %d, duration = %.2fs\n", m_nFrameCnt, fDuration);

    if(m_pPacket != NULL)
    {
        av_packet_free(&m_pPacket);
    }
    if(m_pFrame != NULL)
    {
        av_frame_free(&m_pFrame);
    }
    if(m_pCodecCtx != NULL)
    {
        avcodec_free_context(&m_pCodecCtx);
    }
    if(m_pSwrCtx != NULL)
    {
        swr_free(&m_pSwrCtx);
    }

    for(int i = 0; i < AV_NUM_DATA_POINTERS; i++)
    {
        if(m_aPcmPointer[i] != NULL)
        {
            delete[] m_aPcmPointer[i];
        }
    }
    if(m_pPcmData != NULL)
    {
        delete[] m_pPcmData;
    }
    if(m_pOutData != NULL)
    {
        delete[] m_pOutData;
    }
}

bool Pcm2AAC::Init(int i_nPcmSampleRate, AVSampleFormat i_ePcmSampleFmt, int i_nPcmChannels)
{
    m_nPcmSampleRate = i_nPcmSampleRate;
    m_ePcmFormat = i_ePcmSampleFmt;
    m_nPcmChannel = i_nPcmChannels;

    avcodec_register_all();

    // 寻找AAC的编码器
    m_pCodec = avcodec_find_encoder(AV_CODEC_ID_AAC);
    if(m_pCodec == NULL)
    {
        fprintf(stderr, "Codec not found\n");
        return false;
    }

    // 初始化编码器上下文, 主要是通道数, 采样率, 采样格式
    m_pCodecCtx = avcodec_alloc_context3(m_pCodec);
    if(m_pCodecCtx == NULL)
    {
        fprintf(stderr, "Could not allocate audio codec context\n");
        return false;
    }
    m_pCodecCtx->channels = m_nPcmChannel;
    m_pCodecCtx->channel_layout = av_get_default_channel_layout(m_nPcmChannel);
    m_pCodecCtx->sample_rate = m_nPcmSampleRate;
    // 新的FFmpeg, 只支持AV_SAMPLE_FMT_FLTP这一种AAC音频格式
    m_pCodecCtx->sample_fmt = AV_SAMPLE_FMT_FLTP;
    m_pCodecCtx->bit_rate = 64000;
    // Allow the use of the experimental AAC encoder
    m_pCodecCtx->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL;

    // 打开编码器
    if(avcodec_open2(m_pCodecCtx, m_pCodec, NULL) < 0)
    {
        fprintf(stderr, "Could not open codec\n");
        return false;
    }

    // 这个编码器的frame_size很关键, 表示每个声道每次只能编码frame_size个采样点, m_pFrame->nb_samples不能比它大
    // 否则会报错: more samples than frame size (avcodec_encode_audio2)
    printf("m_pCodecCtx->frame_size = %d\n", m_pCodecCtx->frame_size);
    m_nFrameSize = m_pCodecCtx->frame_size;

    // av_packet_alloc() 为一个NULL指针分配内存并将其值置为默认
    m_pPacket = av_packet_alloc();
    if(m_pPacket == NULL)
    {
        return false;
    }

    m_pFrame = av_frame_alloc();
    if(m_pFrame == NULL)
    {
        return false;
    }

    // 设置格式转换
    m_pSwrCtx = swr_alloc_set_opts(NULL, m_pCodecCtx->channel_layout, m_pCodecCtx->sample_fmt, m_pCodecCtx->sample_rate,
        m_pCodecCtx->channel_layout, m_ePcmFormat, m_nPcmSampleRate, 0, NULL);
    if(m_pSwrCtx == NULL)
    {
        fprintf(stderr, "Could not allocate resample context\n");
        return false;
    }

    if(swr_init(m_pSwrCtx) < 0)
    {
        fprintf(stderr, "Could not open resample context\n");
        return false;
    }

    return true;
}

void Pcm2AAC::AddData(char *i_pData, int i_nSize)
{
    int ret = 0;
    int nSampleSize = 0;

    memcpy(m_pPcmData + m_nPcmSize, i_pData, i_nSize);
    m_nPcmSize += i_nSize;
    nSampleSize = av_get_bytes_per_sample(m_ePcmFormat);
    if(m_nPcmSize <= (nSampleSize * m_nFrameSize * m_nPcmChannel))
    {
        return;
    }
    memcpy(m_aPcmPointer[0], m_pPcmData, nSampleSize * m_nFrameSize * m_nPcmChannel);

    m_nPcmSize -= (nSampleSize * m_nFrameSize * m_nPcmChannel);
    memcpy(m_pPcmData, m_pPcmData + nSampleSize * m_nFrameSize * m_nPcmChannel, m_nPcmSize);

    m_pFrame->pts = m_s64Pts;
    m_s64Pts += m_nFrameSize; // fixme
    m_pFrame->nb_samples = m_nFrameSize;
    m_pFrame->format = m_pCodecCtx->sample_fmt;
    m_pFrame->channel_layout = m_pCodecCtx->channel_layout;
    m_pFrame->sample_rate = m_pCodecCtx->sample_rate;
    if(av_frame_get_buffer(m_pFrame, 0) < 0)
    {
        fprintf(stderr, "Could not allocate audio data buffers\n");
        return ;
    }

    // 转换格式
    if(swr_convert(m_pSwrCtx, m_pFrame->extended_data, m_pFrame->nb_samples, (const uint8_t**)m_aPcmPointer, m_pFrame->nb_samples) < 0)
    {
        fprintf(stderr, "Could not convert input samples (error )\n");
        if(m_pFrame != NULL)
        {
            av_frame_unref(m_pFrame);
        }
        return ;
    }

    // 放进编码器
    ret = avcodec_send_frame(m_pCodecCtx, m_pFrame);
    if(ret < 0)
    {
        fprintf(stderr, "Error sending the frame to the encoder\n");
        if(m_pFrame != NULL)
        {
            av_frame_unref(m_pFrame);
        }
        return;
    }
    if(m_pFrame != NULL)
    {
        av_frame_unref(m_pFrame);
    }
}

void Pcm2AAC::FlushData(void)
{
    // 发送NULL表示刷新packet, 意味着流的结束
    avcodec_send_frame(m_pCodecCtx, NULL);
}

bool Pcm2AAC::GetData(char *&o_pData, int &o_nSize)
{
    // 从编码器取出编码完的数据
    int ret = avcodec_receive_packet(m_pCodecCtx, m_pPacket);
    if(ret < 0)
    {
        return false;
    }

    // 由于编码后的AAC流没有ADTS头, 会导致保存的文件无法被播放器识别, 所以需要手动写入ADTS头
    // 在每个AAC原始流前面加上一个7字节的ADTS头
    AddADTS(m_pPacket->size + ADTS_HEADER_SIZE);
    m_nFrameCnt++;

    memcpy(m_pOutData + ADTS_HEADER_SIZE, m_pPacket->data, m_pPacket->size);
    o_nSize = m_pPacket->size + ADTS_HEADER_SIZE;
    o_pData = m_pOutData;
    av_packet_unref(m_pPacket);
    return true;
}

// ADTS全称是(Audio Data Transport Stream), 是AAC的一种十分常见的传输格式
void Pcm2AAC::AddADTS(int i_nPktLen)
{
    int nProfile = 1;   // AAC LC, 低复杂度规格
    int nFreqIdx = 4;   // default 44.1kHz
    int nChanCfg = m_nPcmChannel;

    if(i_nPktLen > ADTS_MAX_FRAME_BYTES)
    {
        fprintf(stderr, "ADTS frame size too large: %d (max %d)\n", i_nPktLen, ADTS_MAX_FRAME_BYTES);
        return;
    }

    switch(m_nPcmSampleRate)
    {
        case 96000:
            nFreqIdx = 0;
            break;
        case 88200:
            nFreqIdx = 1;
            break;
        case 64000:
            nFreqIdx = 2;
            break;
        case 48000:
            nFreqIdx = 3;
            break;
        case 44100:
            nFreqIdx = 4;
            break;
        case 32000:
            nFreqIdx = 5;
            break;
        case 24000:
            nFreqIdx = 6;
            break;
        case 22050:
            nFreqIdx = 7;
            break;
        case 16000:
            nFreqIdx = 8;
            break;
        case 12000:
            nFreqIdx = 9;
            break;
        case 11025:
            nFreqIdx = 10;
            break;
        case 8000:
            nFreqIdx = 11;
            break;
        case 7350:
            nFreqIdx = 12;
            break;
        default:
            break;
    }

    // Fill in ADTS header, 7 bytes
    m_pOutData[0] = 0xFF;
    m_pOutData[1] = 0xF1;
    m_pOutData[2] = ((nProfile) << 6) + (nFreqIdx << 2) + (nChanCfg >> 2);
    m_pOutData[3] = (((nChanCfg & 3) << 6) + (i_nPktLen >> 11));
    m_pOutData[4] = ((i_nPktLen & 0x7FF) >> 3);
    m_pOutData[5] = (((i_nPktLen & 7) << 5) + 0x1F);
    m_pOutData[6] = 0xFC;
}

main.cpp 仅供参考,详见注释

#include <stdio.h>
#include "Pcm2AAC.h"

int main(int argc, char *argv[])
{
    // 输入的PCM文件
    FILE *pInFile = fopen("./in.pcm", "rb");
    // 输出的AAC文件
    FILE *pOutFile = fopen("./out.aac", "ab");
    char aFrameBuf[1024] = {0}; // AVCodecContext->frame_size = 1024
    char *pOutData = NULL;
    int nReadSize = 0;
    int nOutSize = 0;
    Pcm2AAC objPcm2AAC;
    bool bEOF = false;

    // 我手头上的PCM是AV_SAMPLE_FMT_S16格式(有符号的16位), 采样率44100, 两声道的
    if(!(objPcm2AAC.Init(44100, AV_SAMPLE_FMT_S16, 2)))
    {
        printf("objPcm2AAC Init error\n");
        return -1;
    }

    printf("converting start\n");

    while(true)
    {
        nReadSize = fread(aFrameBuf, 1, sizeof(aFrameBuf), pInFile);
        if(nReadSize > 0)
        {
            // 把PCM数据放进编码器
            objPcm2AAC.AddData(aFrameBuf, nReadSize);
        }
        else
        {
            // 刷新编码器, 标志着PCM流结束
            objPcm2AAC.FlushData();
            bEOF = true;
        }

        while(true)
        {
            // 从编码器取出AAC数据
            if(!(objPcm2AAC.GetData(pOutData, nOutSize)))
            {
                break;
            }
            fwrite(pOutData, 1, nOutSize, pOutFile);
        }

        if(bEOF)
        {
            break;
        }
    }

    printf("converting end\n");

    fclose(pOutFile);
    fclose(pInFile);
    return 0;
}

编译
arm-linux-g++ -std=c++11 main.cpp Pcm2Aac.cpp -o pcm2aac -I/ffmpeg/include -I./ -L/ffmpeg/lib -lpthread -lavdevice -lavformat -lavcodec -lavutil -lswresample

运行
~# ./pcm2aac
m_pCodecCtx->frame_size = 1024
converting start
converting end
nFramesPerSecond = 43
fFrameTime = 23.26ms
This AAC total frames = 2628, duration = 61.12s
[aac @ 0xf33350] Qavg: 678.742

得到的out.aac文件可以拿去PC上播放

in.pcm可以到这里去取
https://download.csdn.net/download/cfl927096306/12584097

参考
https://blog.csdn.net/g0415shenw/article/details/81606813

### 如何在 Qt 中使用 FFmpegPCM 音频文件转换为 AAC 格式 要实现将 PCM 文件转换为 AAC 的功能,可以利用 FFmpeg 提供的强大音视频处理能力。以下是详细的说明: #### 使用 FFmpeg 进行音频编码 FFmpeg 是一个多用途工具库,支持多种多媒体格式之间的转换。通过调用其命令行接口或者直接集成到应用程序中,能够完成复杂的媒体操作。 对于将 PCM 转换为 AAC 的过程,主要涉及以下几个方面: - 初始化输入流并读取原始 PCM 数据。 - 设置输出参数以指定目标编解码器(此处为 AAC 编解码器)以及比特率等配置项。 - 执行实际的数据压缩与写入磁盘的操作[^1]。 #### 实现步骤概述 下面是一个基于 C++ 和 Qt 平台下嵌套 FFmpeg 库来执行上述任务的例子代码片段: ```cpp #include <QCoreApplication> extern "C" { #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> } int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); AVCodec* codec; AVCodecContext* c= NULL; int i, out_size, size, x, y; av_register_all(); /* find the MP2 encoder */ codec = avcodec_find_encoder(AV_CODEC_ID_AAC); if (!codec) { fprintf(stderr, "codec not found\n"); exit(1); } c= avcodec_alloc_context3(codec); c->bit_rate = 64000; // Set bit rate. c->sample_fmt = AV_SAMPLE_FMT_FLTP; // Specify sample format (floating point planar). c->sample_rate = 44100; // Define sampling frequency as per requirement. c->channels = 2; // Mono or stereo channel count. /* open it */ if (avcodec_open2(c, codec, nullptr) < 0){ fprintf(stderr,"could not open codec\n"); return -1 ; } uint8_t* buffer; int bufferSize = av_samples_get_buffer_size(NULL,c->channels, c->frame_size,c->sample_fmt,1); buffer=(uint8_t*)malloc(bufferSize); while(get_input_data(&buffer,&size)){ send_packet_to_codec(context,packet); receive_encoded_frames(output_file); } free(buffer); avcodec_close(c); av_free(c); } ``` 此段程序展示了如何初始化一个简单的 AAC 编码环境,并准备好了用于存储已编码样本帧的缓冲区。注意这里省略了一些细节函数定义比如 `get_input_data`, 它们应该依据具体应用场景定制开发[^2]. #### 关键点解释 - **AVSampleFormat**: 表示采样数据的形式,在本例中选择了浮点型平面布局(`AV_SAMPLE_FMT_FLTP`),这是许多现代软件合成器使用的标准形式之一。 - **Frame Size & Buffer Management**: 计算所需内存大小以便容纳单个音频帧的所有样品值;这一步骤非常重要因为不当分配可能导致越界访问错误甚至崩溃应用进程。 #### 注意事项 当把这样的逻辑融入 GUI 框架如 Qt 当中的时候,请务必考虑线程安全问题,尤其是如果打算让长时间运行的任务不影响界面响应的话,则需采用多线程机制分离耗时计算部分同 UI 更新控制之外独立运作[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cfl927096306

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值