记录 Qt+ffmpeg编码麦克风音频数据

该代码示例展示了如何在Qt环境中捕获麦克风的PCM原始数据,使用ffmpeg进行重采样和编码成AAC音频,并通过RTP发送。关键步骤包括设置音频格式,创建重采样上下文,初始化音频编码器,以及处理音频帧和编码数据包。
摘要由CSDN通过智能技术生成

说明

记录一下Qt环境捕捉麦克风原始pcm数据,通过ffmpeg编程成AAC音频的核心代码。此部分代码摘抄自个人的RTP实时音视频传输项目,实现了子线程采集pcm并编码为AVPacket的功能。

代码

void AudioCapturer::run()
{
    if(rtpSender == nullptr)
    {
        qDebug() << "外部RTP发送者指针无效";
        return;
    }

    //设置Qt音频录制参数
    int sampleRate = 44100;
    int channels = 2;
    int sampleByte = 2;
    AVSampleFormat inSampleFmt = AV_SAMPLE_FMT_S16;

    QAudioFormat recordFmt;
    recordFmt.setSampleRate(sampleRate);
    recordFmt.setChannelCount(channels);
    recordFmt.setSampleSize(sampleByte * 8);
    recordFmt.setCodec("audio/pcm");
    recordFmt.setByteOrder(QAudioFormat::LittleEndian);
    recordFmt.setSampleType(QAudioFormat::UnSignedInt);
    QAudioDeviceInfo info = QAudioDeviceInfo::defaultInputDevice();
    if (!info.isFormatSupported(recordFmt))
    {
        qDebug() << "Audio format not support!";
        recordFmt = info.nearestFormat(recordFmt);
    }
    QAudioInput *audioInput = new QAudioInput(recordFmt);

    //ffmpeg -h encoder=aac 查询自带编码器仅支持AV_SAMPLE_FMT_FLTP 大多数AAC编码器都采用平面布局数据格式 可以提高数据访问效率和缓存命中率 加快编码效率
    AVSampleFormat outSampleFmt = AV_SAMPLE_FMT_FLTP;
    rtpSender->info.sampleRate = sampleRate;
    rtpSender->info.format = outSampleFmt;
    rtpSender->info.channel = channels;
    rtpSender->info.hasAudio = 1;
    rtpSender->setAudio = true;

    //设置重采样
    SwrContext *audioSwrCtx = swr_alloc_set_opts(NULL, av_get_default_channel_layout(channels), outSampleFmt, sampleRate,
                                                       av_get_default_channel_layout(channels), inSampleFmt, sampleRate, 0, NULL);
    if (!audioSwrCtx)
    {
        qDebug() << "swr_alloc_set_opts failed!";
        return;
    }

    int ret = swr_init(audioSwrCtx);
    if (ret != 0)
    {
        qDebug() << "swr_init error" << getAVError(ret);
        swr_free(&audioSwrCtx);
        return;
    }

    AVFrame *swrFrame = av_frame_alloc();
    swrFrame->format = outSampleFmt;
    swrFrame->channels = channels;
    swrFrame->channel_layout = av_get_default_channel_layout(channels);
    swrFrame->nb_samples = 1024;
    ret = av_frame_get_buffer(swrFrame, 0);
    if (ret != 0)
    {
        qDebug() << "av_frame_get_buffer audio" << getAVError(ret);
        return;
    }
    ret = av_frame_make_writable(swrFrame);
    if (ret != 0)
    {
        qDebug() << "av_frame_is_writable audio" << getAVError(ret);
        return;
    }

    //初始化音频编码器相关
    AVCodec *audioEncoder = avcodec_find_encoder(AV_CODEC_ID_AAC);
    if (!audioEncoder)
    {
        qDebug() << "avcodec_find_encoder AV_CODEC_ID_AAC failed!";
        return;
    }

    AVCodecContext *audioCodecCtx = avcodec_alloc_context3(audioEncoder);
    if (!audioCodecCtx)
    {
        qDebug() << "avcodec_alloc_context3 AV_CODEC_ID_AAC failed!";
        return;
    }

    audioCodecCtx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
    audioCodecCtx->bit_rate = 64*1024;
    audioCodecCtx->sample_rate = sampleRate;
    audioCodecCtx->sample_fmt = outSampleFmt;
    audioCodecCtx->channels = channels;
    audioCodecCtx->channel_layout = av_get_default_channel_layout(channels);
    audioCodecCtx->frame_size = 1024;
    AVDictionary *dict = NULL;
    av_dict_set(&dict, "tune", "zerolatency", 0);

    //打开音频编码器
    ret = avcodec_open2(audioCodecCtx, audioEncoder, NULL);
    if (dict)
    {
        av_dict_free(&dict);
        dict = NULL;
    }
    if (ret != 0)
    {
        qDebug() << "avcodec_open2 audio error" << getAVError(ret);
        return;
    }

    //一帧pcm原始数据的字节数
    int pcmSize = av_get_bytes_per_sample((AVSampleFormat)inSampleFmt) * channels * audioCodecCtx->frame_size;
    char *pcmBuf = new char[pcmSize];

    //采用实时时间戳
    int64_t startTime = av_gettime();

    //音频数据开始捕获
    QIODevice *audioIO = audioInput->start();
    qDebug() << "init AudioCapturer Sucess" << sampleRate << channels << outSampleFmt;

    while(isRun)
    {
        if (audioInput->bytesReady() >= pcmSize)
        {
            //捕获一帧pcm原始数据
            int size = 0;
            while (size != pcmSize)
            {
                int len = audioIO->read(pcmBuf + size, pcmSize - size);
                if (len < 0)
                    break;
                size += len;
            }

            //重采样后进行编码处理
            const uint8_t *indata[AV_NUM_DATA_POINTERS] = {0};
            indata[0] = (uint8_t *)pcmBuf;
            swr_convert(audioSwrCtx, swrFrame->data, swrFrame->nb_samples, indata, swrFrame->nb_samples);

            //添加时间戳 实时时间戳方式
            int64_t audioPts = av_rescale_q(av_gettime() - startTime, AVRational{ 1, AV_TIME_BASE }, audioCodecCtx->time_base);
            swrFrame->pts = audioPts;

            //进行音频编码得到编码数据AVPacket
            int ret = avcodec_send_frame(audioCodecCtx, swrFrame);
            if (ret != 0)
                continue;

            while(1)
            {
                AVPacket *packet = av_packet_alloc();
                ret = avcodec_receive_packet(audioCodecCtx, packet);
                if (ret != 0)
                {
                    av_packet_free(&packet);
                    break;
                }

                ReadyPacket *readyPacket = new ReadyPacket();
                readyPacket->type = PacketAudio;
                readyPacket->packet = packet;
                rtpSender->writePacket(readyPacket);
            }
        }

        QThread::msleep(1);
    }

    //释放资源
    audioInput->stop();
    audioIO->close();
    delete audioInput;

    avcodec_free_context(&audioCodecCtx);
    audioCodecCtx = nullptr;
    swr_free(&audioSwrCtx);
    audioSwrCtx = nullptr;
    av_frame_free(&swrFrame);
    swrFrame = nullptr;
    delete[] pcmBuf;
    pcmBuf = nullptr;
}

敬告:该系列的课程在抓紧录制更新中,敬请大家关注。敬告: 该系列的课程涉及:FFmpeg,WebRTC,SRS,Nginx,Darwin,Live555,等。包括:音视频、流媒体、直播、Android、视频监控28181、等。  我将带领大家一起来学习:采集麦克风、PCM重采样、AAC或MP3音频编码存储、并直播。具体内容包括:1.FFmpeg采集麦克风并存储为PCM。2.PCM重采样原理及实战。3.采集麦克风并实时转码AAC或MP3并保存文件。4.采集麦克风并实时转码AAC并可以直播。 音视频与流媒体是一门很复杂的技术,涉及的概念、原理、理论非常多,很多初学者不学 基础理论,而是直接做项目,往往会看到c/c++的代码时一头雾水,不知道代码到底是什么意思,这是为什么呢? 因为没有学习音视频和流媒体的基础理论,就比如学习英语,不学习基本单词,而是天天听英语新闻,总也听不懂。所以呢,一定要认真学习基础理论,然后再学习播放器、转码器、非编、流媒体直播、视频监控、等等。 梅老师从事音视频与流媒体行业18年;曾在永新视博、中科大洋、百度、美国Harris广播事业部等公司就职,经验丰富;曾亲手主导广电直播全套项目,精通h.264/h.265/aac,曾亲自参与百度app上的网页播放器等实战产品。 目前全身心自主创业,主要聚焦音视频+流媒体行业,精通音视频加密、流媒体在线转码快编等热门产品。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

你是周小哥啊

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

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

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

打赏作者

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

抵扣说明:

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

余额充值