ffmpeg将PCM格式数据转为MP3格式

此文章在QT中实现,使用QAudioInput获取麦克风数据后,逐帧解码为MP3格式音频保存。


PCM的格式是AV_SAMPLE_FMT_S16,MP3的格式是AV_SAMPLE_FMT_S16P,所以在转换时需要先进行格式转化,可以通过swr_convert函数进行转换。


实现的核心代码如下:

1、检查MP3解码器是否支持此类型。

static int check_sample_fmt(const AVCodec *codec, enum AVSampleFormat sample_fmt)
{
    const enum AVSampleFormat *p = codec->sample_fmts;

    while (*p != AV_SAMPLE_FMT_NONE) {
        if (*p == sample_fmt)
            return 1;
        p++;
    }
    return 0;
}

2、解码器初始化

void AudioInput::PCM2MP3(){
    AVCodecContext *codecCtx =nullptr;
    const AVCodec *codec = nullptr;
    AVFrame *frame = nullptr;
    AVPacket *pkt = nullptr;

    frame = av_frame_alloc();
    pkt = av_packet_alloc();

    //==========Output information============
    //查找编码器
    codec = avcodec_find_encoder(AV_CODEC_ID_MP3);
    if(codec==nullptr){
        qInfo() <<"Cannot find audio encoder.\n";
        return ;
    }

    codecCtx = avcodec_alloc_context3(codec);
    codecCtx->bit_rate = 64000;
    codecCtx->sample_fmt = AV_SAMPLE_FMT_S16P;
    if (!check_sample_fmt(codec, codecCtx->sample_fmt)) {
        qInfo() << "Encoder does not support sample format %s",
            av_get_sample_fmt_name(codecCtx->sample_fmt);
        exit(1);
    }
    codecCtx->sample_rate = Samplerate;
    codecCtx->ch_layout =AV_CHANNEL_LAYOUT_STEREO;

    //打开编码器
    if(avcodec_open2(codecCtx,codec,nullptr) < 0)
        qInfo() << "Open encode error!";

    frame->nb_samples = codecCtx->frame_size;
    frame->format = codecCtx->sample_fmt;
    av_channel_layout_copy(&frame->ch_layout, &codecCtx->ch_layout);

    qInfo() << "start init swr";
    // MP3数不支持AV_SAMPLE_FMT_S16解码,所以将数据转化为AV_SAMPLE_FMT_S16P
    struct SwrContext *swrContext = swr_alloc();
    swr_alloc_set_opts2(&swrContext,
                        &codecCtx->ch_layout,
                        AV_SAMPLE_FMT_S16P,
                        Samplerate,
                        &codecCtx->ch_layout,
                        AV_SAMPLE_FMT_S16,// PCM源文件的采样格式
                        Samplerate,0,nullptr);

    // 重采样初始化
    swr_init(swrContext);
    qInfo() << "end init swr";

    /* 分配空间 */
    uint8_t **convert_data = static_cast<uint8_t**>(calloc(2, static_cast<int>(sizeof(convert_data))));
    av_samples_alloc(convert_data,nullptr,2,codecCtx->frame_size,
                     codecCtx->sample_fmt,0);

    uint8_t *frameBuf = static_cast<uint8_t *>(av_malloc(sizeof(uint8_t *)));
    int length = av_samples_get_buffer_size(nullptr, codecCtx->ch_layout.nb_channels, codecCtx->frame_size, codecCtx->sample_fmt, 1);
    int readsize = length / (codecCtx->ch_layout.nb_channels * av_get_bytes_per_sample(codecCtx->sample_fmt));

    //存储所有数据
    QByteArray arrayAll;
    qInfo() << "start encode";
    while(che){
        //输入一帧数据的长度 4608
        QByteArray array = audioDevice->read(length);
        //使用length有杂音,不使用length会丢失最后几帧数据
        arrayAll += array;

        if(arrayAll.size() > length)
        {
            //对数据进行处理,方便后面音频编码
            array = arrayAll.left(length);
            arrayAll.remove(0,length);

            //读PCM:特意注意读取的长度,否则可能出现转码之后声音变快或者变慢
            frameBuf = reinterpret_cast<uint8_t *>(array.data()) ;
            const uint8_t *tmpframe = frameBuf;
            swr_convert(swrContext,
                        convert_data,codecCtx->frame_size,
                        &tmpframe,readsize);
            //输出一帧数据的长度
            //双通道赋值(输出的AAC为双通道)
            frame->data[0] = convert_data[0];
            frame->data[1] = convert_data[1];

            ENcode(codecCtx,frame,pkt);
        }
    }
    qInfo() << "end encode";
    ENcode(codecCtx,nullptr,pkt);
    swr_free(&swrContext);
    avcodec_free_context(&codecCtx);
    av_free(frame);
    return;
}

3、解码过程。

void AudioInput::ENcode(AVCodecContext *cdc_ctx, AVFrame *frame, AVPacket *pkt){
    int ret=0;
    /* send the frame for encoding */
    ret = avcodec_send_frame(cdc_ctx, frame);
    if (ret < 0) {
        qDebug() << "Error sending the frame to the encoder\n";
        return;
    }

    /* read all the available output packets (in general there may be any
         * number of them */
    while (ret >= 0) {
        ret = avcodec_receive_packet(cdc_ctx, pkt);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
            continue;
        else if (ret < 0) {
            qDebug() << "Error encoding audio frame\n";
            continue;
        }
        av_packet_unref(pkt);
    }

    return;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

布丁小站

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

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

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

打赏作者

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

抵扣说明:

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

余额充值