FFmpeg实现的简单视频编码器(YUV to H264)

FFmpeg实现的从YUV编码到H264的简单视频编码器,FFmpeg version = 4.3.1

encoder.h

#ifndef __ENCODER__
#define __ENCODER__

#include <iostream>
#include <string>
extern "C" {
    
    
#include "libavformat/avformat.h"
#include "libavutil/imgutils.h"
#include "libavutil/opt.h"
}

#define FRAME_PRE_SECOND 25

class CEncoder {
    
    
public:
	CEncoder(std::string& infile, std::string& outfile);
	~CEncoder();
	void initialize();
	void compression(AVFrame* frame);
	void encode();
	
private:
	//*********** encode ***********
	std::string enInfilePath;
	std::string enOutfilePath;
	FILE* enInfile;
	uint8_t* rBuf;

	//*********** ffmpeg ***********
	AVFormatContext* pFmtCtx;
	AVCodecContext* pCodecCtx;
	AVStream* vStream;
	AVCodec* pCodec;
	AVPacket* pkt;
};

#endif //__ENCODER__

encoder.cpp

#include "encoder.h"

CEncoder::CEncoder(std::string& infile, std::string& outfile) : 
	//encode
	enInfilePath(infile),
	enOutfilePath(outfile),
	pFmtCtx(nullptr),
	vStream(nullptr),
	pCodecCtx(nullptr),
	pCodec(nullptr),
	rBuf(nullptr),
	enInfile(nullptr) {
    
     }

CEncoder::~CEncoder() {
    
    
	//free read file buffer
	if (!rBuf) {
    
    
		av_free(rBuf);
	}
	//free packet
	if (!pkt) {
    
    
		av_packet_free(&pkt);
	}
	//free AVCodecContext
	if (!pCodecCtx) {
    
    
		avcodec_close(pCodecCtx);
		avcodec_free_context(&pCodecCtx);
	}
	//free AVFormatContext
	if (!pFmtCtx) {
    
    
		avformat_free_context(pFmtCtx);
	}
}

void CEncoder::initialize() {
    
    
	//Allocate an AVFormatContext for an output format
	if (avformat_alloc_output_context2(&pFmtCtx, nullptr, nullptr, enOutfilePath.c_str()) < 0) {
    
    
		av_log(nullptr, AV_LOG_FATAL, "Could not allocate context.\n");
		return;
	}
	//Create and initialize a AVIOContext for accessing the resource indicated by url
	if (avio_open2(&pFmtCtx->pb, enOutfilePath.c_str(), AVIO_FLAG_READ_WRITE, nullptr, nullptr) < 0) {
    
    
		av_log(nullptr, AV_LOG_FATAL, "Failed to open output file.\n");
		return;
	}
	//Add a new stream to a media file
	vStream = avformat_new_stream(pFmtCtx, nullptr);
	if (!vStream) {
    
    
		av_log(nullptr, AV_LOG_FATAL, "Failed to create stream channel.\n");
		return;
	}
	//Set codec parameter
	vStream->codecpar->width = 640;
	vStream->codecpar->height = 360;
	vStream->codecpar->bit_rate = 876627;
	vStream->codecpar->format = AV_PIX_FMT_YUV420P;
	vStream->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
	vStream->codecpar->codec_id = pFmtCtx->oformat->video_codec;
	//Allocate an AVCodecContext and set its fields to default values
	pCodecCtx = avcodec_alloc_context3(nullptr);
	if (!pCodecCtx) {
    
    
		av_log(nullptr, AV_LOG_FATAL, "Could not allocate a codec context.\n");
		return;
	}
	//Fill the codec context based on the values from the supplied codec parameters
	if (avcodec_parameters_to_context(pCodecCtx, vStream->codecpar) < 0) {
    
    
		av_log(nullptr, AV_LOG_FATAL, "Codec not fill the codec context.\n");
		return;
	}
	//Set codec context parameter
	pCodecCtx->gop_size = 12;
	pCodecCtx->max_b_frames = 0;
	pCodecCtx->time_base = AVRational{
    
     1, FRAME_PRE_SECOND };
	pCodecCtx->framerate = AVRational{
    
     FRAME_PRE_SECOND, 1 };
	if (pCodecCtx->codec_id == AV_CODEC_ID_H264) {
    
    
		//最大和最小量化系数
		pCodecCtx->qmin = 10;
		pCodecCtx->qmax = 51;
		pCodecCtx->qcompress = 0.6;
		av_opt_set(pCodecCtx->priv_data, "preset", "slow", 0);
	}
	//Find a registered encoder with a matching codec ID
	pCodec = avcodec_find_encoder(pCodecCtx->codec_id);
	if (!pCodec) {
    
    
		av_log(nullptr, AV_LOG_FATAL, "Can not find encoder.\n");
		return;
	}
	//Open codec
	if (avcodec_open2(pCodecCtx, pCodec, nullptr) != 0) {
    
    
		av_log(nullptr, AV_LOG_FATAL, "Open encoder failure.\n");
		return;
	}
	//Allocate an AVPacketand set its fields to default values
	pkt = av_packet_alloc();
	if (!pkt) {
    
    
		av_log(nullptr, AV_LOG_FATAL, "Failed to initialize AVpacket.\n");
		return;
	}
	//Open input file
	enInfile = fopen(enInfilePath.c_str(), "rb");
	if (!enInfile) {
    
    
		av_log(nullptr, AV_LOG_FATAL, "Failed to open file.\n");
		return;
	}
	//Allocate space to read the input file
	rBuf = static_cast<uint8_t*>(av_malloc(av_image_get_buffer_size(pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, 1)));
}

void CEncoder::compression(AVFrame* frame) {
    
    
	//Supply a raw video or audio frame to the encoder
	int res = avcodec_send_frame(pCodecCtx, frame);
	if (res < 0) {
    
    
		av_log(nullptr, AV_LOG_FATAL, "Error sending a frame for encoding.\n");
		return;
	}
	while (res >= 0) {
    
    
		//Read encoded data from the encoder
		res = avcodec_receive_packet(pCodecCtx, pkt);
		if (res == AVERROR(EAGAIN) || res == AVERROR_EOF) {
    
    
			break;
		}
		if (res < 0) {
    
    
			av_log(nullptr, AV_LOG_FATAL, "Error during encoding.\n");
			return;
		}
		if (res >= 0) {
    
    
			pkt->stream_index = 0;
			//Write a packet to an output media file
			av_write_frame(pFmtCtx, pkt);
			av_packet_unref(pkt);
		}
	}
}

void CEncoder::encode() {
    
    
	initialize();
	//Allocate an AVFrame and set its fields to default values
	AVFrame* oframe = av_frame_alloc();
	if (!oframe) {
    
    
		av_log(nullptr, AV_LOG_FATAL, "Failed to allocate an AVFrame.\n");
		return;
	}
	oframe->width = pCodecCtx->width;
	oframe->height = pCodecCtx->height;
	oframe->format = pCodecCtx->pix_fmt;
	//Allocate new buffer(s) for audio or video data
	if (av_frame_get_buffer(oframe, 0) < 0) {
    
    
		av_log(nullptr, AV_LOG_FATAL, "Could not allocate the video frame data.\n");
		return;
	}
	//Allocate the stream private data and write the stream header to an output media file
	avformat_write_header(pFmtCtx, nullptr);
	int frameNum = 1;
	//loop encode
	while (!feof(enInfile)) {
    
    
		if (fread(rBuf, 1, pCodecCtx->width * pCodecCtx->height * 3 / 2, enInfile) < 0) {
    
    
			av_log(nullptr, AV_LOG_FATAL, "Failed to read niput video data.\n");
			break;
		}
		if (feof(enInfile)) {
    
    
			break;
		}
		oframe->data[0] = rBuf;
		oframe->data[1] = rBuf + pCodecCtx->width * pCodecCtx->height;
		oframe->data[2] = rBuf + pCodecCtx->width * pCodecCtx->height * 5 / 4;
		//Presentation Time Stamp
		oframe->pts = frameNum;
		compression(oframe);
		av_log(nullptr, AV_LOG_INFO, "frame pts: %d\n", frameNum++);
	}
	compression(nullptr);
	//Write the stream trailer to an output media file and free the file private data
	av_write_trailer(pFmtCtx);
	fclose(enInfile);
	av_frame_free(&oframe);
}

如有侵权,请联系删除,如有错误,欢迎大家指正,谢谢

猜你喜欢

转载自blog.csdn.net/xiao_ma_nong_last/article/details/110180903