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中调试结果

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值