1、设置编码格式:
AVCodecID codec_id = AV_CODEC_ID_H264;
2、打开编码器
视频编码器可以分为软件编码器、cuvid编码器、 qsv编码器,由于硬件编码器效率高,所以优先打开硬件编码器,具体实现如下(三个编码器打开函数查看最后的补充内容)。
//打开视频编码器
bool VideoEncoder::openVideoEncoder(const AVCodecID &codec_id)
{
bool isSucceed = false;
bool isHardWareEncoderOpened = false;
//尝试打开cuvid编码器器
isHardWareEncoderOpened = openHardEncoder_Cuvid(codec_id);
//cuvid打开失败了 继续尝试 qsv
if (!isHardWareEncoderOpened)
isHardWareEncoderOpened = openHardEncoder_Qsv(codec_id);
//尝试打开硬件解码器失败了 改用软解码
if (!isHardWareEncoderOpened)
isSucceed = openSoftEncoder(codec_id);
else
isSucceed = true;
return isSucceed;
}
3、进行编码
int ret = avcodec_send_frame(pCodecCtx, picture);
if (ret != 0)
{
char buff[128]={0};
av_strerror(ret, buff, 128);
fprintf(stderr, "Error sending a frame for encoding! (%s)\n", buff);
continue;
}
while (0 == avcodec_receive_packet(pCodecCtx, &pkt))
{
bool isKeyFrame = pkt.flags & AV_PKT_FLAG_KEY; //判断是否关键帧
T_NALU_TYPE naluType = T_NALU_H264;
VideoFramePtr framePtr = std::make_shared<VideoFrame>(); //创建VideoFrame类型的智能指针
framePtr->setNalu(pkt.data + 4, pkt.size - 4, naluType); //剔除4个字节的起始码
RtpSenderManager->sendVideo(framePtr, node.time); //发送数据
}
av_packet_unref(&pkt);
av_free(node.buffer);
补充:
- 打开软编码器函数
bool VideoEncoder::openSoftEncoder(const AVCodecID &codec_id)
{
bool isSucceed = false;
fprintf(stderr,"open software encoder... \n");
pCodec = avcodec_find_encoder(codec_id);
if (pCodec == nullptr)
{
fprintf(stderr, "Codec not found.\n");
isSucceed = false;
}
else
{
pCodecCtx = avcodec_alloc_context3(pCodec);
pCodecCtx->thread_count = 8;
AVDictionary *param = setEncoderParam(codec_id);
///打开解码器
if (avcodec_open2(pCodecCtx, pCodec, ¶m) < 0)
{
avcodec_close(pCodecCtx);
avcodec_free_context(&pCodecCtx);
pCodecCtx = nullptr;
isSucceed = false;
fprintf(stderr,"Could not open codec.\n");
}
else
isSucceed = true;
}
return isSucceed;
}
- 打开硬件编码器(intel)
bool VideoEncoder::openHardEncoder_Qsv(const AVCodecID &codec_id)
{
bool isSucceed = false;
fprintf(stderr,"open hardware encoder cuvid...\n");
//查找硬件解码器
char hardWareDecoderName[32] = {0};
if (AV_CODEC_ID_H264 == codec_id)
sprintf(hardWareDecoderName, "h264_qsv");
else if (AV_CODEC_ID_HEVC == codec_id)
sprintf(hardWareDecoderName, "hevc_qsv");
if (strlen(hardWareDecoderName) > 0)
{
pCodec = avcodec_find_encoder_by_name(hardWareDecoderName);
if (pCodec != NULL)
{
pCodecCtx = avcodec_alloc_context3(pCodec);
AVDictionary *param = setEncoderParam(codec_id);
if (avcodec_open2(pCodecCtx, pCodec, ¶m) < 0) //打开解码器
{
avcodec_close(pCodecCtx);
avcodec_free_context(&pCodecCtx);
pCodecCtx = nullptr;
isSucceed = false;
fprintf(stderr,"Could not open codec %s\n",hardWareDecoderName);
}
else
{
isSucceed = true;
fprintf(stderr,"open codec %s succeed! %d %d\n",hardWareDecoderName,pCodec->id,pCodecCtx->codec_id);
}
}
else
fprintf(stderr,"Codec %s not found.\n",hardWareDecoderName);
}
return isSucceed;
}
- 打开硬件编码器(英伟达)
bool VideoEncoder::openHardEncoder_Cuvid(const AVCodecID &codec_id)
{
bool isSucceed = false;
fprintf(stderr,"open hardware encoder cuvid...\n");
///查找硬件解码器
char hardWareDecoderName[32] = {0};
if (AV_CODEC_ID_H264 == codec_id)
sprintf(hardWareDecoderName, "h264_nvenc");
else if (AV_CODEC_ID_HEVC == codec_id)
sprintf(hardWareDecoderName, "hevc_nvenc");
if (strlen(hardWareDecoderName) > 0)
{
pCodec = avcodec_find_encoder_by_name(hardWareDecoderName);
if (pCodec != nullptr)
{
pCodecCtx = avcodec_alloc_context3(pCodec);
AVDictionary *param = setEncoderParam(codec_id);
if (avcodec_open2(pCodecCtx, pCodec, ¶m) < 0) //打开解码器
{
avcodec_close(pCodecCtx);
avcodec_free_context(&pCodecCtx);
pCodecCtx = nullptr;
isSucceed = false;
fprintf(stderr,"Could not open codec %s\n",hardWareDecoderName);
}
else
{
isSucceed = true;
fprintf(stderr,"open codec %s succeed! %d %d\n",hardWareDecoderName,pCodec->id,pCodecCtx->codec_id);
}
}
else
fprintf(stderr,"Codec %s not found.\n",hardWareDecoderName);
}
return isSucceed;
}