解码mp4文件分别存储为pcm,yuv文件

// 解码分别写入对应文件
#include "myLog.h"
#include <iostream>

extern "C"
{
#include <libavformat\avformat.h>
#include <libavutil\avutil.h>
#include <libavcodec\avcodec.h>
#include <libavutil\imgutils.h>
#include <libavutil\samplefmt.h>
}
#pragma comment(lib, "avformat.lib")
#pragma comment(lib, "avcodec.lib")

class Decoder
{
public:
	Decoder(std::string src_filename, std::string dst_audio_filename, std::string dst_video_filename);
	~Decoder();

	// 解封装
	int demuxer();

	// 解码
	int decodec();

	// 打印音视频播放格式
	void print_audio_play_str();
private:
	// 打开解码器
	int open_decodec();

	// 解码pkt
	int decode_pkt(AVCodecContext* codec_ctx, AVPacket* pkt);

	// 输出视频帧
	int output_video_frame(AVFrame* frame);

	// 输出音频帧
	int output_audio_frame(AVFrame* frame);

	// 获取音频格式
	int get_format_from_sample_fmt(std::string& fmtStr, enum AVSampleFormat sample_fmt);
private:
	AVFormatContext* ifmt_ctx_;					// 格式上下文
	AVCodecContext*  video_decode_ctx_;			// 视频解码器上下文
	AVCodecContext*  audio_decode_ctx_;			// 音频解码器上下文

	int video_idx_;								// 视频索引						
	AVStream* video_stream_;					// 视频流

	int audio_idx_;								// 音频索引
	AVStream* audio_stream_;					// 音频流

	std::string src_filename_;					// 输入文件路径
	std::string dst_audio_filename_;			// 输出音频pcm文件路径
	std::string dst_video_filename_;			// 输出视频yuv路径							
	FILE* dst_video_fp_;
	FILE* dst_audio_fp_;

	AVFrame* frame_;
	uint8_t* video_dst_data_[4];				// 记录video一行数据
	int video_dst_buf_size_;					// 分配的真实buf大小 
	int linesize_[4];							// 每行数据大小(YUV RGB 第四个为保留字段)
};

Decoder::Decoder(std::string src_filename, std::string dst_audio_filename, std::string dst_video_filename)
: ifmt_ctx_(nullptr)
, video_decode_ctx_(nullptr)
, audio_decode_ctx_(nullptr)
, video_stream_(nullptr)
, audio_stream_(nullptr)
, dst_video_fp_(nullptr)
, dst_audio_fp_(nullptr)
, frame_(nullptr)
, src_filename_(src_filename)
, dst_audio_filename_(dst_audio_filename)
, dst_video_filename_(dst_video_filename)
{
	for (int i = 0; i < 4; i++)
	{
		video_dst_data_[i] = NULL;
	}
}

Decoder::~Decoder()
{
	// 释放相关资源
	if (ifmt_ctx_)
	{
		avformat_close_input(&ifmt_ctx_);
	}
	if (audio_decode_ctx_)
	{
		avcodec_free_context(&audio_decode_ctx_);
	}
	if (video_decode_ctx_)
	{
		avcodec_free_context(&video_decode_ctx_);
	}
	if (dst_audio_fp_)
	{
		fclose(dst_audio_fp_);
	}	
	if (dst_video_fp_)
	{
		fclose(dst_video_fp_);
	}
	if (frame_)
	{
		av_frame_free(&frame_);
	}
	if (video_dst_data_)
	{
		av_free(video_dst_data_[0]);	// 释放数据指针(其它三个指针无需释放) data[0]指向缓冲区开始位置,data[1] - data[3]也是指向data[0]的数据指针
	}
}

int Decoder::demuxer()
{
	// 1. 打开文件
	int nRet = avformat_open_input(&ifmt_ctx_, src_filename_.c_str(), NULL, NULL);
	if (nRet < 0)
	{
		LOG_WARNING("avformat_open_input error\n");
		return -1;
	}

	// 2. 查找流信息
	nRet = avformat_find_stream_info(ifmt_ctx_, NULL);
	if (nRet < 0)
	{
		avformat_close_input(&ifmt_ctx_);
		LOG_WARNING("avformat_find_stream_info error\n");
		return -2;
	}

	// 3. 获取音视频流索引
	audio_idx_ = av_find_best_stream(ifmt_ctx_, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
	video_idx_ = av_find_best_stream(ifmt_ctx_, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);

	return 0;
}

int Decoder::open_decodec()
{
	// 打开音频解码器
	if (audio_idx_ >= 0)
	{
		audio_stream_ = ifmt_ctx_->streams[audio_idx_];
		// 查找解码器
		AVCodec* audio_decodec = avcodec_find_decoder(audio_stream_->codecpar->codec_id);
		if (!audio_decodec)
		{
			LOG_WARNING("avcodec_find_decoder error\n");
			return AVERROR(ENOMEM);
		}

		// 给解码上下文分配内存
		audio_decode_ctx_ = avcodec_alloc_context3(audio_decodec);
		if (!audio_decode_ctx_)
		{
			LOG_WARNING("avcodec_alloc_context3 error\n");
			return AVERROR(ENOMEM);
		}

		// 拷贝解码参数到解码上下文
		int nRet = avcodec_parameters_to_context(audio_decode_ctx_, audio_stream_->codecpar);
		if (nRet < 0)
		{
			LOG_WARNING("avcodec_parameters_to_context error\n");
			return -1;
		}
		// 打开解码器
		nRet = avcodec_open2(audio_decode_ctx_, audio_decodec, NULL);
		if (nRet < 0)
		{
			LOG_WARNING("avcodec_open2 error\n");
			return -2;
		}

		dst_audio_fp_ = fopen(dst_audio_filename_.c_str(), "wb");
		if (!dst_audio_fp_)
		{
			LOG_WARNING("fopen dst_audio_filename_ error\n");
			return -1;
		}
	}

	// 打开视频解码器
	if (video_idx_ >= 0)
	{
		video_stream_ = ifmt_ctx_->streams[video_idx_];
		// 查找解码器
		AVCodec* video_decodec = avcodec_find_decoder(video_stream_->codecpar->codec_id);
		if (!video_decodec)
		{
			LOG_WARNING("avcodec_find_decoder error\n");
			return AVERROR(ENOMEM);
		}

		// 给解码上下文分配内存
		video_decode_ctx_ = avcodec_alloc_context3(video_decodec);
		if (!video_decode_ctx_)
		{
			LOG_WARNING("avcodec_alloc_context3 error\n");
			return AVERROR(ENOMEM);
		}

		// 拷贝解码参数到解码上下文
		int nRet = avcodec_parameters_to_context(video_decode_ctx_, video_stream_->codecpar);
		if (nRet < 0)
		{
			LOG_WARNING("avcodec_parameters_to_context error\n");
			return -1;
		}
		// 打开解码器
		nRet = avcodec_open2(video_decode_ctx_,video_decodec, NULL);
		if (nRet < 0)
		{
			LOG_WARNING("avcodec_open2 error\n");
			return -2;
		}

		video_dst_buf_size_ = av_image_alloc(video_dst_data_, linesize_,
			video_decode_ctx_->width, video_decode_ctx_->height, video_decode_ctx_->pix_fmt, 1);

		dst_video_fp_ = fopen(dst_video_filename_.c_str(), "wb");
		if (!dst_video_fp_)
		{
			LOG_WARNING("fopen dst_video_filename_ error\n");
			return -1;
		}
	}
}

int Decoder::decodec()
{
	// 打开解码器
	int nRet = open_decodec();
	if (nRet < 0)
	{
		LOG_WARNING("open_decodec error\n");
		return -666;
	}

	AVPacket* pkt = av_packet_alloc();
	av_init_packet(pkt);
	pkt->data = NULL;
	pkt->size = 0;

	while (av_read_frame(ifmt_ctx_, pkt) >= 0)
	{
		if (pkt->stream_index == audio_idx_)	// 音频包
		{
			nRet = decode_pkt(audio_decode_ctx_, pkt);
		}
		else if (pkt->stream_index == video_idx_) // 视频包
		{
			nRet = decode_pkt(video_decode_ctx_, pkt);
		}
		av_packet_unref(pkt);
		if (nRet < 0)
		{
			break;
		}
	}

	// 冲刷解码器
	if (video_decode_ctx_)
	{
		decode_pkt(video_decode_ctx_, NULL);
	}
	if (audio_decode_ctx_)
	{
		decode_pkt(video_decode_ctx_, NULL);
	}
	return 0;
}

int Decoder::decode_pkt(AVCodecContext* codec_ctx, AVPacket* pkt)
{
	int nRet = avcodec_send_packet(codec_ctx, pkt);
	if (nRet < 0)
	{
		LOG_WARNING("avcodec_send_packet fflush codec\n");
		return -1;
	}

	// 一个包可能对应多个frame
	frame_ = av_frame_alloc();
	while (nRet >= 0)
	{
		nRet = avcodec_receive_frame(codec_ctx, frame_);
		if (nRet < 0)
		{
			if (nRet == AVERROR_EOF || nRet == AVERROR(EAGAIN))
				return 0;
			return nRet;
		}
		// 写数据
		if (codec_ctx->codec->type == AVMEDIA_TYPE_AUDIO)
		{
			nRet = output_audio_frame(frame_);
		}
		else if (codec_ctx->codec->type == AVMEDIA_TYPE_VIDEO)
		{
			nRet = output_video_frame(frame_);
		}
		av_frame_unref(frame_);
		if (nRet < 0)
			return nRet;
	}
}

int Decoder::output_video_frame(AVFrame* frame)
{
	if (video_dst_buf_size_ < 0)
	{
		LOG_WARNING("av_image_alloc error\n");
		return -2;
	}
	// 写入帧到文件
	if (frame->width != video_decode_ctx_->width ||
		frame->height != video_decode_ctx_->height ||
		frame->format != video_decode_ctx_->pix_fmt)
	{
		LOG_WARNING("frame and video_decode_ctx_ error\n");
		return -3;
	}
	// 填充数据到 video_dst_data
	av_image_copy(video_dst_data_, linesize_,
		(const uint8_t**)(frame->data), frame->linesize,
		video_decode_ctx_->pix_fmt, video_decode_ctx_->width, video_decode_ctx_->height);
	size_t write_video_size = fwrite(video_dst_data_[0], 1, video_dst_buf_size_, dst_video_fp_);
	fflush(dst_video_fp_);
	return 0;
}

int Decoder::output_audio_frame(AVFrame* frame)
{
	// 计算一帧音频数据的bytes
	size_t a_sample_point_bytes = frame->nb_samples * av_get_bytes_per_sample((AVSampleFormat)frame->format);
	// 写入数据到文件
	fwrite(frame->extended_data[0], 1, a_sample_point_bytes, dst_audio_fp_);
	fflush(dst_audio_fp_);
	return 0;
}

int Decoder::get_format_from_sample_fmt(std::string& fmtStr, enum AVSampleFormat sample_fmt)
{
	struct sample_fmt_entry {
		enum AVSampleFormat sample_fmt; 
		std::string fmt_be;
		std::string fmt_le;
	} sample_fmt_entries[] = {
		{ AV_SAMPLE_FMT_U8, "u8", "u8" },
		{ AV_SAMPLE_FMT_S16, "s16be", "s16le" },
		{ AV_SAMPLE_FMT_S32, "s32be", "s32le" },
		{ AV_SAMPLE_FMT_FLT, "f32be", "f32le" },
		{ AV_SAMPLE_FMT_DBL, "f64be", "f64le" },
	};

	for (int i = 0; i < FF_ARRAY_ELEMS(sample_fmt_entries); i++) {
		struct sample_fmt_entry *entry = &sample_fmt_entries[i];
		if (sample_fmt == entry->sample_fmt) {
			fmtStr = AV_NE(entry->fmt_be, entry->fmt_le);
			return 0;
		}
	}

	fprintf(stderr, "sample format %s is not supported as output format\n", av_get_sample_fmt_name(sample_fmt));
	return -1;
}

void Decoder::print_audio_play_str()
{
	if (audio_stream_) {
		enum AVSampleFormat sfmt = audio_decode_ctx_->sample_fmt;
		int n_channels = audio_decode_ctx_->channels;
		std::string fmtStr;

		if (av_sample_fmt_is_planar(sfmt))	// 是planar格式
		{
			const char *packed = av_get_sample_fmt_name(sfmt);
			LOG_INFO("Warning: the sample format the decoder produced is planar "
				"(%s). This example will output the first channel only.\n",
				packed ? packed : "?");
			sfmt = av_get_packed_sample_fmt(sfmt);
			n_channels = 1;
		}

		int ret = -1;
		if ((ret = get_format_from_sample_fmt(fmtStr, sfmt)) < 0)
			return;

		LOG_INFO("Play the output audio file with the command:\n"
			"ffplay -f %s -ac %d -ar %d %s\n",
			fmtStr.c_str(), n_channels, audio_decode_ctx_->sample_rate,
			dst_audio_filename_.c_str());

		LOG_INFO("play video yuv:\nffplay -video_size %dx%d -f rawvideo -pixel_format yuv420p %s",
			video_decode_ctx_->width, video_decode_ctx_->height, dst_video_filename_.c_str());
	}
}

int main()
{
	std::string src_filename = "./MediaFile/HTA_13s.mp4";	// input_5s
	std::string dst_audio_filename = "./MediaFile/HTA_13s_decodec_audio.pcm"; // HTA_13s_decodec_audio
	std::string dst_video_filename = "./MediaFile/HTA_13s_decodec_video.yuv"; // HTA_13s_decodec_video

	Decoder decoder(src_filename, dst_audio_filename, dst_video_filename);
	// 解复用
	int nRet = decoder.demuxer();
	if (nRet < 0)
	{
		LOG_WARNING("main demuxer error\n");
		return -666;
	}
	// 解码写入到文件
	nRet = decoder.decodec();
	if (nRet < 0)
	{
		LOG_WARNING("main decodec error\n");
		return -777;
	}

	LOG_INFO("decode finish, write file success\n");
	decoder.print_audio_play_str();
	
	getchar();
	return 0;
}

注意:使用av_image_alloc为一个char* data[4]的数组分配内存时,其内部只是为data[0]分配内存,其余的data[1] - data[3]指针指向data[0]的某个位置(根据像素格式的不同)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

石小浪♪

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

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

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

打赏作者

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

抵扣说明:

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

余额充值