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);
}
如有侵权,请联系删除,如有错误,欢迎大家指正,谢谢