.h
#pragma once
#include "headers.h"
extern "C"
{
#include <libavcodec/avcodec.h>
#include "libavformat/avformat.h"
#include "libavutil/avutil.h"
#include "libswscale/swscale.h"
}
class CX264Encoder
{
public:
CX264Encoder();
virtual ~CX264Encoder();
public:
bool InitEncoder(int nWidth, int nHeight, int nRate, int nFps);
void UninitEncoder();
// *pOutSize > 0 ==> success
void Encode(BYTE* pInBuf, BYTE** ppOutBuf, int* pOutSize);
protected:
AVFrame* m_pRGBFrame;
AVFrame* m_pYUVFrame;
AVCodecContext* m_pCtx;
AVCodec* m_pCodecH264;
SwsContext* m_pSwsCtx;
BYTE* m_pYUVBuf;
BYTE* m_pOutBuf;
int m_nOutSize;
};
#include "X264Encoder.h"
#pragma comment(lib, "avcodec.lib")
#pragma comment(lib, "avformat.lib")
#pragma comment(lib, "avutil.lib")
#pragma comment(lib, "swscale.lib")
CX264Encoder::CX264Encoder()
{
m_pRGBFrame = NULL;
m_pYUVFrame = NULL;
m_pCtx = NULL;
m_pCodecH264 = NULL;
m_pSwsCtx = NULL;
m_pYUVBuf = NULL;
m_pOutBuf = NULL;
m_nOutSize = 0;
}
CX264Encoder::~CX264Encoder()
{
UninitEncoder();
}
bool CX264Encoder::InitEncoder(int nWidth, int nHeight, int nRate, int nFps)
{
avcodec_init();
av_register_all();
avcodec_register_all();
m_pRGBFrame = avcodec_alloc_frame();
m_pYUVFrame = avcodec_alloc_frame();
if (!m_pRGBFrame || !m_pYUVFrame)
return false;
m_pCodecH264 = avcodec_find_encoder(CODEC_ID_H264);
if (!m_pCodecH264)
return false;
m_pCtx = avcodec_alloc_context();
if (!m_pCtx)
return false;
m_pCtx->bit_rate = nRate;
m_pCtx->width = nWidth;
m_pCtx->height = nHeight;
m_pCtx->time_base.num = 1;
m_pCtx->time_base.den = nFps;
m_pCtx->gop_size = 10; // 至少获取 10 帧编码才成功
m_pCtx->max_b_frames = 1;
m_pCtx->thread_count = 1;
m_pCtx->pix_fmt = PIX_FMT_YUV420P;
if (avcodec_open(m_pCtx, m_pCodecH264) < 0)
return false;
int nYUVSize = (nWidth * nHeight * 3) / 2;
m_pYUVBuf = (BYTE*)malloc(nYUVSize);
if (!m_pYUVBuf)
return false;
m_nOutSize = nWidth * nHeight * 3;
m_pOutBuf = (BYTE*)malloc(m_nOutSize);
if (!m_pOutBuf)
return false;
// 格式转换初始化 BGR -> YUV
m_pSwsCtx = sws_getContext(
nWidth, nHeight, PIX_FMT_BGR24/*PIX_FMT_RGB24*/,
nWidth, nHeight, PIX_FMT_YUV420P,
SWS_POINT, NULL, NULL, NULL);
if (!m_pSwsCtx)
return false;
return true;
}
void CX264Encoder::UninitEncoder()
{
if (m_pRGBFrame)
{
av_free(m_pRGBFrame);
m_pRGBFrame = NULL;
}
if (m_pYUVFrame)
{
av_free(m_pYUVFrame);
m_pYUVFrame = NULL;
}
if (m_pYUVBuf)
{
free(m_pYUVBuf);
m_pYUVBuf = NULL;
}
if (m_pOutBuf)
{
free(m_pOutBuf);
m_pOutBuf = NULL;
}
if (m_pCtx)
{
avcodec_close(m_pCtx);
av_free(m_pCtx);
m_pCtx = NULL;
}
}
void CX264Encoder::Encode(BYTE* pInBuf, BYTE** ppOutBuf, int* pOutSize)
{
*ppOutBuf = NULL;
*pOutSize = 0;
int nWidth = m_pCtx->width;
int nHeight = m_pCtx->height;
avpicture_fill((AVPicture*)m_pRGBFrame, (uint8_t*)pInBuf, PIX_FMT_BGR24/*PIX_FMT_RGB24*/, nWidth, nHeight);
avpicture_fill((AVPicture*)m_pYUVFrame, (uint8_t*)m_pYUVBuf, PIX_FMT_YUV420P, nWidth, nHeight);
// BGR -> RGB
/*m_pRGBFrame->data[0] += m_pRGBFrame->linesize[0] * (nHeight - 1);
m_pRGBFrame->linesize[0] *= -1;
m_pRGBFrame->data[1] += m_pRGBFrame->linesize[1] * (nHeight / 2 - 1);
m_pRGBFrame->linesize[1] *= -1;
m_pRGBFrame->data[2] += m_pRGBFrame->linesize[2] * (nHeight / 2 - 1);
m_pRGBFrame->linesize[2] *= -1;*/
// RGB -> YUV
sws_scale(m_pSwsCtx, m_pRGBFrame->data, m_pRGBFrame->linesize, 0, nHeight, m_pYUVFrame->data, m_pYUVFrame->linesize);
// YUV -> H264
*pOutSize = avcodec_encode_video(m_pCtx, m_pOutBuf, m_nOutSize, m_pYUVFrame);
*ppOutBuf = m_pOutBuf;
}