FFmpeg编码流程
FFmpeg版本为 FFmpeg6
1.FFmpeg的编码流程比较固定,只有一些细节需要自己设计,这里为了展示方便,一些可以封装的代码并没有封装
2.编码h265或者h264只需要更改pCodec的初始化函数参数即可
3.该代码只读取灰度图,如果要读取彩图,只需要给pFrame的UV分量设置数据即可
4.编码出来的数据存放在 pPkt中,无论是想写入文件还是发送出去,都可以根据这个pPkt->data 来获取数据
void FFmpeg_Encode( const QVector< QString > &vecFileNames )
{
// 1. 初始化编码所需要的对象
AVCodecContext *pCodecCtx = nullptr;
AVCodec *pCodec = nullptr;
AVFrame *pFrame = nullptr;
AVPacket *pPkt = nullptr;
// 2. 打开编码器 flows: 软编,英伟达独显编码, 因特尔核显编码( HD620以上? )
pCodec = avcodec_find_encoder( AV_CODEC_ID_HEVC );
pCodec = avcodec_find_encoder_by_name( "hevc_nvenc" );
pCodec = avcodec_find_encoder_by_name( "hevc_qsv" );
//确保编码器打开成功
if( nullptr == pCodec ){
std::cout << "find encoder failed..." << std::endl;
return;
}
// 3. 创建编码上下文, 并确保创建上下文成功
pCodecCtx = avcodec_alloc_context3( pCodec );
if( nullptr == pCodecCtx ){
std::cout << "create codec context failed..." << std::endl;
return;
}
// 4. 设置编码信息, 帧高,帧宽, 像素格式等等信息
pCodecCtx->width = 1920;
pCodecCtx->height = 1280;
pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;
// 5. 打开编码器, 第三个参数是设置一些编码额外的参数,如果遇到编码延迟,可通过第三个参数来降低延迟,并不能全完解决延迟?
int ret = avcodec_open2( pCodecCtx, pCodec, nullptr );
if( 0 != ret ){
std::cout << "open codec failed..." << std::endl;
return;
}
// 6. 初始化frame信息 和 packet 信息
pFrame = av_frame_alloc();
pFrame->width = pCodecCtx->width;
pFrame->height = pCodecCtx->height;
pFrame->format = pCodecCtx->pix_fmt;
//这一步好像可以不用?
ret = av_frame_get_buffer( pFrame, 0 );
pPkt = av_packet_alloc();
// 7.初始化文件对象
std::ofstream ofs;
ofs.open( "./test.h265", std::ios::binary );
// 8.读取要编码的图片
for( auto &ele : vecFileNames ){
QImage img( ele);
img = img.convertToFormat( QImage::Format_Grayscale8 );
uchar *imgData = img.bits();
int imgSize = img.byteCount();
//此处设置 Y 分量, UV分量在调用处,根据图片尺寸信息设置
memcpy( pFrame->data[ 0 ], imgData, imgSize );
//设置UV分量
memset( pFrame->data[ 1 ], 0x80, pCodecCtx->width * pCodecCtx->height / 4 );
memset( pFrame->data[ 2 ], 0x80, pCodecCtx->width * pCodecCtx->height / 4 );
// 发送要编码的frame给编码器
ret = avcodec_send_frame( pCodecCtx, pFrame );
// 大于 0 表示有数据返回
while( ret >= 0 ){
// 接收编码器编码出来的帧
ret = avcodec_receive_packet( pCodecCtx, pPkt );
// ffmpeg 有可能会做帧缓存,导致编码延迟,编码一帧数据,可能需要多帧原始图像
if ( ret == AVERROR( EAGAIN ) ){
break;
}
// 编码错误
if( ret == AVERROR_EOF ){
break;
}
//写入文件
ofs.write( (char*)(pPkt->data), pPkt->size );
}
// 9.释放相关对象
av_packet_free( &pPkt );
av_frame_free( &pFrame );
avcodec_free_context( &pCodecCtx );
}
}