ice_bear221 2023-08-03 18:22 采纳率: 0%
浏览 3

CLion和VS中avcodec_receive_frame()获取结果不同

1. 介绍

在提取音视频文件中音频的PCM数据时,使用avcodec_receive_frame()函数进行解码时,遇到了一些问题,代码在Visual Studio 2022中运行结果符合预期,但是在CLion中运行时,获取的AVFrame有错误,和VS中获得的结果不一样。

FFMpeg 5.1.2

2. 源码

  1. Utils.h
#pragma once

#define _CRT_SECURE_NO_WARNINGS

extern "C" {
#include <libavutil/error.h>
}

static char* wrap_av_err2str(int errnum) {
    static char str[256] = {0};
    return av_make_error_string(str, sizeof(str), errnum);
}
  1. AudioDecoder2.h
#pragma once

extern "C"
{
#include "libavutil/log.h"
#include "libavutil/imgutils.h"
#include "libavutil/samplefmt.h"
#include "libavutil/timestamp.h"
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
};
#include "Utils.h"
#include <cinttypes>

class AudioDecoder2
{
public:
    AudioDecoder2();
    AudioDecoder2(const char* src_filename, const char* dst_filename);
    ~AudioDecoder2();
    int start();

private:
    int ret;
    char src_filename[256];
    char dst_filename[256];
    FILE* dst_fd = NULL;

    AVFormatContext* ifmt_ctx = NULL;
    AVCodecContext* audio_dec_ctx = NULL;
    const AVCodec* audio_dec = NULL;
    AVStream* audio_stream = NULL;
    int audio_stream_index = -1;
    AVFrame* frame = NULL;
    AVPacket* packet = NULL;
};

  1. AudioDecoder2.cpp
#include "AudioDecoder2.h"

AudioDecoder2::AudioDecoder2(const char* src_filename, const char* dst_filename) {
    sscanf(src_filename, "%s", this->src_filename);
    sscanf(dst_filename, "%s", this->dst_filename);
}

int AudioDecoder2::start() {
    // 设置日志输出级别
    av_log_set_level(AV_LOG_INFO);

    // 打开输入文件
    ret = avformat_open_input(&ifmt_ctx, src_filename, NULL, NULL);
    if (ret < 0)
    {
        av_log(NULL, AV_LOG_ERROR, "Can't open source file:%s\n", wrap_av_err2str(ret));
        return -1;
    }

    // 读取一部分数据获得一些相关信息
    ret = avformat_find_stream_info(ifmt_ctx, NULL);
    if (ret < 0)
    {
        av_log(NULL, AV_LOG_ERROR, "Failed to find stream information: %s\n", wrap_av_err2str(ret));
        return -1;
    }

    // 查找流
    audio_stream_index = -1;
    audio_stream_index = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
    if (audio_stream_index < 0)
    {
        av_log(NULL, AV_LOG_DEBUG, "Failed to find the best audio stream!\n");
        return AVERROR(EINVAL);
    }

    // 获取流
    audio_stream = ifmt_ctx->streams[audio_stream_index];

    // 通过数据流查找对应的解码器
    audio_dec = avcodec_find_decoder(audio_stream->codecpar->codec_id);
    if (audio_dec == NULL)
    {
        av_log(NULL, AV_LOG_ERROR, "Failed to find codec.\n");
        return AVERROR(EINVAL);
    }

    // 创建解码器上下文
    audio_dec_ctx = avcodec_alloc_context3(audio_dec);
    if (audio_dec_ctx == NULL)
    {
        av_log(NULL, AV_LOG_ERROR, "Failed to allocate the codec context.\n");
        return AVERROR(ENOMEM);
    }

    // 从输入流中拷贝对应的参数到解码器中
    ret = avcodec_parameters_to_context(audio_dec_ctx, audio_stream->codecpar);
    if (ret < 0)
    {
        av_log(NULL, AV_LOG_ERROR, "Failed to copy codec parameters to decoder context.\n");
        return ret;
    }

    // 打开解码器
    ret = avcodec_open2(audio_dec_ctx, audio_dec, NULL);
    if (ret < 0)
    {
        av_log(NULL, AV_LOG_ERROR, "Failed to open codec.\n");
        return ret;
    }

    // 创建输出文件
    dst_fd = fopen(dst_filename, "wb");
    if (!dst_fd)
    {
        av_log(NULL, AV_LOG_ERROR, "Could not open destination file %s\n", dst_filename);
        return -1;
    }

    // 分配帧
    frame = av_frame_alloc();
    if (frame == NULL)
    {
        av_log(NULL, AV_LOG_ERROR, "Failed to allocate frame.\n");
        return AVERROR(ENOMEM);
    }

    // 分配数据包
    packet = av_packet_alloc();
    if (packet == NULL)
    {
        av_log(NULL, AV_LOG_ERROR, "Failed to allocate packet.\n");
        return AVERROR(ENOMEM);
    }

    // 读取音频流的数据包,并解码
    while (av_read_frame(ifmt_ctx, packet) >= 0)
    {
        if (packet->stream_index == audio_stream_index)
        {
            ret = avcodec_send_packet(audio_dec_ctx, packet);
            if (ret < 0)
            {
                av_log(NULL, AV_LOG_ERROR, "Failed to decode packet.\n");
                return ret;
            }

            // 对数据进行解码输出
            while (ret >= 0)
            {
                ret = avcodec_receive_frame(audio_dec_ctx, frame);
                if (ret < 0)
                {
                    if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN))
                    {
                        break;
                    }
                    else
                    {
                        break;
                    }
                }
                if (ret == 0)
                {
                    // 将内容输出到文件
                    av_log(NULL, AV_LOG_INFO, "pts:%10" PRId64"\t packet size:%d\n",
                        packet->pts, packet->size);

                    size_t unpadded_linesize = frame->nb_samples * av_get_bytes_per_sample((enum AVSampleFormat)frame->format);
                    fwrite(frame->extended_data[0], 1, unpadded_linesize, dst_fd);
                }

                av_frame_unref(frame);
            }
        }
        av_packet_unref(packet);
    }

    // 刷新解码器
    while (true)
    {
        if (!(audio_dec->capabilities & AV_CODEC_CAP_DELAY))
        {
            return 0;
        }

        ret = avcodec_send_packet(audio_dec_ctx, packet);
        if (ret < 0)
        {
            //            av_log(NULL, AV_LOG_ERROR, "Failed to decode packet.\n");
            //            return ret;
            break;
        }

        // 对数据进行解码输出
        while (ret >= 0)
        {
            ret = avcodec_receive_frame(audio_dec_ctx, frame);
            if (ret < 0)
            {
                if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN))
                {
                    break;
                }
            }

            // 将内容输出到文件
            size_t unpadded_linesize = frame->nb_samples * av_get_bytes_per_sample((enum AVSampleFormat)frame->format);
            fwrite(frame->extended_data[0], 1, unpadded_linesize, dst_fd);

            av_frame_unref(frame);
        }

        av_packet_unref(packet);
    }

    // 释放资源
    avcodec_free_context(&audio_dec_ctx);
    avformat_close_input(&ifmt_ctx);
    if (dst_fd)
    {
        fclose(dst_fd);
    }
    av_packet_free(&packet);
    av_frame_free(&frame);

    return 0;
}
  1. main.cpp
#include <iostream>
#include "AudioDecoder2.h"
using namespace std;

int main(int argc, char** argv)
{

    char src_filename[20] = "ball_10s.mp4";
    char audio_dst_filename[20] = "ball_10s.pcm";

    AudioDecoder2* audioDecoder2 = new AudioDecoder2(src_filename, audio_dst_filename);
    audioDecoder2->start();
    return 0;
}

3. 问题

  1. Visual Studio 2022调试结果

  1. CLion中调试结果

  • 写回答

1条回答 默认 最新

  • 飞利浦音响BOK专卖 2023-08-08 14:23
    关注

    参观 学习

    评论

报告相同问题?

问题事件

  • 创建了问题 8月3日

悬赏问题

  • ¥15 无法输出helloworld
  • ¥15 高通uboot 打印ubi init err 22
  • ¥20 PDF元数据中的XMP媒体管理属性
  • ¥15 R语言中lasso回归报错
  • ¥15 网站突然不能访问了,上午还好好的
  • ¥15 有没有dl可以帮弄”我去图书馆”秒选道具和积分
  • ¥15 semrush,SEO,内嵌网站,api
  • ¥15 Stata:为什么reghdfe后的因变量没有被发现识别啊
  • ¥15 振荡电路,ADS仿真
  • ¥15 关于#c语言#的问题,请各位专家解答!