如何用FFmpeg/libav获取音频采样数据?解码帧无数据求助
FFmpeg解码音频时AVFrame无法获取解压数据的问题
我正在开发类DAW软件,需要实现音频波形可视化,核心是获取单个音频采样值并绘制。目前尝试读取首个采样值,但使用FFmpeg/libav解码时,无法将解压数据写入AVFrame。参照旧示例写的代码因为FFmpeg版本更新失效,调试发现avcodec_send_packet执行成功,但avcodec_receive_frame返回的Frame无数据。
当前代码逻辑:
- 获取音频文件格式信息
- 从格式中提取音频流信息
- 验证流对应的编解码器为音频类型
- 获取编解码器上下文
- 创建空AVPacket和AVFrame(Packet存压缩数据,Frame存解压数据)
- 读取音频文件首帧到Packet
- 将Packet传入编解码器上下文解码
- 尝试从编解码器上下文接收解压数据到Frame(此步骤失败)
- 分配缓存准备存储采样值(暂未完成)
使用Qt开发的代码如下:
// Step 1 - get the formatting info of the audio file AVFormatContext* format = avformat_alloc_context(); if (avformat_open_input(&format, path.toStdString().c_str(), NULL, NULL) != 0) { qDebug() << "Could not open file " << path; return -1; } // Step 2 - get audio stream info from the format if (avformat_find_stream_info(format, NULL) < 0) { qDebug() << "Could not retrieve stream info from file " << path; return -1; } // Step 3 - check that the codec required for the stream is an audio codec int stream_index = -1; for (unsigned int i=0; i<format->nb_streams; i++) { if (format->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { stream_index = i; break; } } if (stream_index == -1) { qDebug() << "Could not retrieve audio stream from file " << path; return -1; } // Step 4 -get the codec context const AVCodec *codec = avcodec_find_decoder(format->streams[stream_index]->codecpar->codec_id); AVCodecContext *codecContext = avcodec_alloc_context3(codec); avcodec_open2(codecContext, codec, NULL); // Step 5 - create an empty packet and frame to use AVPacket *packet = av_packet_alloc(); AVFrame *frame = av_frame_alloc(); // Step 6 - the format reads the first frame from the audio file into our packet av_read_frame(format, packet); // Step 7 - pass that packet into the codec context to be decoded avcodec_send_packet(codecContext, packet); //Step 8 - pass our frame to the codec context to receive the uncompressed audio data of the first frame avcodec_receive_frame(codecContext, frame); // Step 9 - create a buffer to hold the values and try allocating samples to it from our frame double *buffer; av_samples_alloc((uint8_t**) &buffer, NULL, 1, frame->nb_samples, AV_SAMPLE_FMT_DBL, 0); qDebug () << "packet: " << &packet; qDebug() << "frame: " << frame; qDebug () << "buffer: " << buffer;
问题根源及修正步骤
未拷贝编解码器参数到CodecContext
FFmpeg新版本中,AVStream.codec已被废弃,改用codecpar存储参数。你只创建了CodecContext但未将stream->codecpar的参数拷贝进去,导致解码器缺少关键的音频配置(如采样率、通道数、采样格式等),无法正常解码。
修正:在avcodec_alloc_context3后添加:if (avcodec_parameters_to_context(codecContext, format->streams[stream_index]->codecpar) < 0) { qDebug() << "Failed to copy codec parameters to context"; return -1; }未检查关键函数的返回值
avcodec_send_packet和avcodec_receive_frame的返回值必须检查,它们可能返回EAGAIN(需要更多输入或输出)、AVERROR_EOF等状态,直接忽略会导致错误无法排查。
修正示例:int send_ret = avcodec_send_packet(codecContext, packet); if (send_ret < 0) { qDebug() << "Failed to send packet for decoding: " << av_err2str(send_ret); return -1; } int recv_ret = avcodec_receive_frame(codecContext, frame); if (recv_ret == AVERROR(EAGAIN)) { qDebug() << "Need more packets to decode"; } else if (recv_ret == AVERROR_EOF) { qDebug() << "End of stream"; } else if (recv_ret < 0) { qDebug() << "Failed to receive frame: " << av_err2str(recv_ret); return -1; }未过滤非音频流的Packet
av_read_frame可能读到视频流或其他非音频流的Packet,必须检查packet->stream_index是否等于你找到的stream_index,否则解码无效数据。
修正:读取Packet后添加过滤逻辑:while (av_read_frame(format, packet) >= 0) { if (packet->stream_index != stream_index) { av_packet_unref(packet); continue; } // 执行解码逻辑 break; // 只处理第一个音频Packet }资源初始化与释放的细节
avformat_alloc_context可以不用手动调用,avformat_open_input会自动分配,手动分配后如果打开失败会导致内存泄漏。- 使用完Packet和Frame后,必须调用
av_packet_unref和av_frame_unref释放资源,避免内存泄漏。
修正后的核心代码片段
// Step 4 -get the codec context const AVCodec *codec = avcodec_find_decoder(format->streams[stream_index]->codecpar->codec_id); if (!codec) { qDebug() << "Unsupported codec"; return -1; } AVCodecContext *codecContext = avcodec_alloc_context3(codec); if (!codecContext) { qDebug() << "Failed to allocate codec context"; return -1; } // 拷贝编解码器参数 if (avcodec_parameters_to_context(codecContext, format->streams[stream_index]->codecpar) < 0) { qDebug() << "Failed to copy codec parameters"; avcodec_free_context(&codecContext); return -1; } if (avcodec_open2(codecContext, codec, NULL) < 0) { qDebug() << "Failed to open codec"; avcodec_free_context(&codecContext); return -1; } // Step 5 - create an empty packet and frame to use AVPacket *packet = av_packet_alloc(); AVFrame *frame = av_frame_alloc(); // Step 6 - 读取第一个音频Packet int read_ret; while ((read_ret = av_read_frame(format, packet)) >= 0) { if (packet->stream_index != stream_index) { av_packet_unref(packet); continue; } break; } if (read_ret < 0) { qDebug() << "Failed to read audio packet"; return -1; } // Step 7 - 发送Packet解码 int send_ret = avcodec_send_packet(codecContext, packet); if (send_ret < 0) { qDebug() << "Send packet failed: " << av_err2str(send_ret); av_packet_unref(packet); return -1; } // Step 8 - 接收解码后的Frame int recv_ret = avcodec_receive_frame(codecContext, frame); if (recv_ret < 0) { qDebug() << "Receive frame failed: " << av_err2str(recv_ret); av_packet_unref(packet); return -1; } // 此时frame已包含有效解码数据,可以访问frame->data、frame->nb_samples等字段 qDebug() << "Decoded frame has " << frame->nb_samples << " samples";
内容的提问来源于stack exchange,提问作者Breadsnshreds
相关产品推荐
相关产品推荐

