turner_gao 2023-02-23 22:24 采纳率: 0%
浏览 77
已结题

FFMPEG麦克风编码错误

使用FFMPEG进行麦克风录音编码。
但是执行avcodec_send_frame报错AVERROR(EINVAL)
感觉是编码器参数和frame参数不匹配导致的。
有没有哪位熟悉ffmpeg的专家给看看,是哪的错误。
编码器设置为

    c_ctx->sample_fmt = AV_SAMPLE_FMT_FLTP; //AV_SAMPLE_FMT_S16,AV_SAMPLE_FMT_FLTP;
    c_ctx->channel_layout = AV_CH_LAYOUT_MONO; // 输入音频的CHANNEL LAYOUT
    c_ctx->channels = 1;                         // 输入音频的声道数
    c_ctx->sample_rate = 44100;                  // 输入音频的采样率
    c_ctx->bit_rate = 16000;                         // AAC : 128K   AAV_HE: 64K  AAC_HE_V2: 32K. bit_rate为0时才会查找profile属性值
    c_ctx->profile = FF_PROFILE_AAC_LOW;

frame设置为


    frame = av_frame_alloc();
    frame->nb_samples = 1024;
    frame->format=AV_SAMPLE_FMT_FLTP;
    frame->channel_layout=AV_CH_LAYOUT_MONO;

完整代码如下:

#include "audiodecode.h"

#include <QMessageBox>
#include <QDebug>

// init context
static AVFormatContext *fmt_ctx = nullptr;
static AVPacket ptk;
static int pktPos = 0;
static AVFrame* frame =nullptr;
static AVPacket *newpkt = nullptr;

static int swr_num = 0;
static uint8_t bufferData[2048];

static AVCodecContext* c_ctx = nullptr;

AudioDecode::AudioDecode( QObject*  parent ):QObject(parent)
{
}

bool AudioDecode::init(){
    // register device
    avdevice_register_all();

    // set logger level
    av_log_set_level(AV_LOG_DEBUG);

    // get input device format
    AVInputFormat *iformat = av_find_input_format("dshow");
    if( !iformat ){
        emit error("get input format fail");
        return false;
    }

    // 输入设备
    QString deviceName = QString::fromLocal8Bit("audio=麦克风 (High Definition Audio Device)");

    AVDictionary *options = nullptr;

    // open audio device
    int r = avformat_open_input(&fmt_ctx, deviceName.toStdString().c_str(), iformat, &options);
    if (r < 0){
        emit error(QString("Failed to open audio device! ret=%1").arg(QString::number(r)));
        return false;
    }

    av_init_packet(&ptk);

    const AVCodec* codec = avcodec_find_encoder(AV_CODEC_ID_AAC);
    if( codec==NULL ){
        emit error("faild to open encoder");
        return false;
    }

    c_ctx = avcodec_alloc_context3(codec);

    c_ctx->sample_fmt = AV_SAMPLE_FMT_FLTP; //AV_SAMPLE_FMT_S16,AV_SAMPLE_FMT_FLTP;
    c_ctx->channel_layout = AV_CH_LAYOUT_MONO; // 输入音频的CHANNEL LAYOUT
    c_ctx->channels = 1;                         // 输入音频的声道数
    c_ctx->sample_rate = 44100;                  // 输入音频的采样率
    c_ctx->bit_rate = 16000;                         // AAC : 128K   AAV_HE: 64K  AAC_HE_V2: 32K. bit_rate为0时才会查找profile属性值
    c_ctx->profile = FF_PROFILE_AAC_LOW;
    // 打开编码器
    if (avcodec_open2(c_ctx, codec, NULL) < 0){
        emit error("failed to open aac");
        return false;
    }

    frame = av_frame_alloc();
    frame->nb_samples = 1024;
    frame->format=AV_SAMPLE_FMT_FLTP;
    frame->channel_layout=AV_CH_LAYOUT_MONO;
    av_frame_get_buffer(frame,0);

    newpkt = av_packet_alloc();//分配编码后的数据空间

    return true;
}

AudioDecode::~AudioDecode(){
    if( frame )
        av_frame_free(&frame);

    if( newpkt )
        av_packet_free(&newpkt);

    if( c_ctx )
        av_free(c_ctx);

    if( fmt_ctx )
        avformat_close_input(&fmt_ctx);
}

AVPacket* AudioDecode::getInput(){
    // 从编码器直接取包
    int ret = avcodec_receive_packet(c_ctx,newpkt);
    if( ret==0 ){
        emit debug(QString("direct audio packet ").append(QString::number(newpkt->size)));
        return newpkt;
    }

    while(true){
        // 没有获取过输入包
        if( ptk.size==0 ){
            ret = av_read_frame(fmt_ctx,&ptk);
            if( ret<0 ){
                emit debug("No new frame");
                return NULL;
            }
            pktPos = 0;
        }

        // 输入包的数据已经编码
        if( pktPos==ptk.size ){
            av_packet_unref(&ptk);
            ret = av_read_frame(fmt_ctx,&ptk);
            if( ret<0 ){
                emit debug("No new frame");
                return NULL;
            }
            pktPos = 0;
        }

        // 拷贝出帧数据到编码缓冲区
        while( swr_num!=2048 && pktPos!=ptk.size ){
            bufferData[swr_num] = ptk.data[pktPos];
            ++swr_num;
            ++pktPos;
        }

        // 编码缓冲区已满
        if( swr_num==2048 ){
            swr_num = 0;
            memcpy((void *)frame->data[0], bufferData, 2048);

            ret = avcodec_send_frame(c_ctx,frame);
            if( ret<0 ){
                switch( ret ){
                case AVERROR(EAGAIN):
                    emit warn(QString("avcodec_send_frame [input is not accepted in the current state - user must read output with avcodec_receive_packet() (once all output is read, the packet should be resent, and the call will not fail with EAGAIN).]"));
                    break;
                case AVERROR_EOF:
                    emit warn(QString("avcodec_send_frame [the encoder has been flushed, and no new frames can be sent to it]"));
                    break;
                case AVERROR(EINVAL):
                    emit warn(QString("avcodec_send_frame [codec not opened, refcounted_frames not set, it is a decoder, or requires flush]"));
                    break;
                case AVERROR(ENOMEM):
                    emit warn(QString("avcodec_send_frame [failed to add packet to internal queue, or similar]"));
                    break;
                default:
                    emit warn(QString("avcodec_send_frame %1").arg(QString::number(ret)));
                }

                return NULL;
            }

            ret = avcodec_receive_packet(c_ctx,newpkt);
            if( ret<0 ){
                emit debug(QString("avcodec_receive_packet %1").arg(QString::number(ret)));
                return NULL;
            }

            emit debug(QString("new audio packet ").append(QString::number(newpkt->size)));
            return newpkt;
        }
    }
}
  • 写回答

4条回答 默认 最新

  • 「已注销」 2023-02-24 07:17
    关注

    参考GPT和自己的思路,在使用FFmpeg进行麦克风录音编码时,当执行 avcodec_send_frame 函数时报错 AVERROR(EINVAL)。从您的代码中可以看出,您使用了 AAC 编码器进行编码,并且配置了音频参数和编码器属性。但是,代码中有几个问题可能导致这个错误。

    首先,您没有给帧分配缓冲区。您应该在分配帧之后调用 av_frame_get_buffer 函数为帧分配内存。例如:

    frame = av_frame_alloc();
    frame->nb_samples = 1024;
    frame->format=AV_SAMPLE_FMT_FLTP;
    frame->channel_layout=AV_CH_LAYOUT_MONO;
    av_frame_get_buffer(frame,0);
    

    接下来,您的编码缓冲区大小为 2048 字节,这似乎是一个不太典型的大小。如果您使用了不正确的缓冲区大小,也可能导致 AVERROR(EINVAL) 错误。建议您将缓冲区大小更改为采样率(例如 44100)的倍数,以确保缓冲区的大小与音频数据相匹配。

    最后,我注意到您使用的是 avcodec_receive_packet 函数直接从编码器中获取输出包,这可能会导致无法正常编码音频。在大多数情况下,您应该按照以下方式编码音频:

    1 使用 av_read_frame 函数从输入设备中获取音频数据。
    2 将音频数据放入帧中,使用 avcodec_send_frame 函数将帧发送到编码器。
    4 通过多次调用 avcodec_receive_packet 函数来获取编码后的输出包。
    您需要使用一个循环来重复执行第 1 步和第 3 步,直到结束录音。在这个过程中,您可以使用信号槽机制在主线程和录音线程之间传递数据。例如,当录音线程获得一个新的输出包时,它可以通过信号发射该包,主线程则通过槽函数接收该包。

    这里是一个可能有用的代码片段,它展示了如何从输入设备中获取音频数据,将其放入帧中并发送到编码器:

    AVPacket pkt;
    av_init_packet(&pkt);
    
    while (true) {
        // read audio frame
        int ret = av_read_frame(fmt_ctx, &pkt);
        if (ret < 0) {
            // end of stream
            break;
        }
    
        // allocate new frame
        AVFrame *frame = av_frame_alloc();
        frame->format = c_ctx->sample_fmt;
        frame->sample_rate = c_ctx->sample_rate;
        frame->channel_layout = c_ctx->channel_layout;
        frame->channels = av_get_channel_layout_nb_channels(frame->channel_layout);
        frame->nb_samples = pkt.size / av_get_bytes_per_sample(c_ctx->sample_fmt) / frame->channels;
    
        // allocate buffer for frame
        av_frame_get_buffer(frame, 0);
    
        // copy audio data to frame
        int num_samples = av 继续
    // copy audio data to frame
    int num_samples = av_get_channel_layout_nb_channels(c_ctx->channel_layout) * frame->nb_samples;
    int ret = av_frame_make_writable(frame);
    if (ret < 0){
    emit error(QString("failed to make frame writable! ret=%1").arg(QString::number(ret)));
    return NULL;
    }
    if (swr_num >= num_samples * av_get_bytes_per_sample(c_ctx->sample_fmt)) {
        swr_num = 0;
        ret = av_samples_fill_arrays(frame->data, frame->linesize, bufferData, 1, frame->nb_samples, c_ctx->sample_fmt, 0);
        if (ret < 0){
            emit error(QString("failed to fill audio samples! ret=%1").arg(QString::number(ret)));
            return NULL;
        }
    
        // set audio frame properties
        frame->pts = av_rescale_q(av_gettime(), { 1, AV_TIME_BASE }, c_ctx->time_base);
        frame->pict_type = AV_PICTURE_TYPE_NONE;
        frame->sample_rate = c_ctx->sample_rate;
    
        // send audio frame to encoder
        ret = avcodec_send_frame(c_ctx, frame);
        if (ret == AVERROR(EINVAL)) {
            emit error(QString("Error sending audio frame to encoder! ret=%1").arg(QString::number(ret)));
            return NULL;
        } else if (ret == AVERROR_EOF) {
            emit error(QString("Error sending audio frame to encoder! ret=%1").arg(QString::number(ret)));
            return NULL;
        }
    }
    
    // no packet output
    return NULL;
    }
    

    如果对您有帮助,请给与采纳,谢谢。

    评论

报告相同问题?

问题事件

  • 已结题 (查看结题原因) 2月26日
  • 创建了问题 2月23日

悬赏问题

  • ¥20 如何在 rocky9.4 部署 CDH6.3.2?
  • ¥35 navicat将excel中的数据导入mysql出错
  • ¥15 rt-thread线程切换的问题
  • ¥20 python忆阻器数字识别
  • ¥15 高通uboot 打印ubi init err 22
  • ¥20 PDF元数据中的XMP媒体管理属性
  • ¥15 R语言中lasso回归报错
  • ¥15 网站突然不能访问了,上午还好好的
  • ¥15 有没有dl可以帮弄”我去图书馆”秒选道具和积分
  • ¥15 semrush,SEO,内嵌网站,api