ffmpeg NVIDIA编解码三:英伟达硬编码
ffmpeg NVIDIA编解码系列
★我的音视频编解码开源项目-FFmpeg-Media-Codec-Pipeline
ffmpeg硬编码流程和软编码流程完全一样:打开编码器、分配编码器上下文,读取视频帧(YUV420P)、avcodec_send_frame送入编码器、avcodec_receive_packet获取编码后的视频帧、关闭编码器。只不过在打开编码器的时候要使用ffmpeg英伟达的编码器(H264:h264_nvenc H265:hevc_nvenc)。
下面是ffmpeg NVIDIA编码流程图:
完整代码:
#include <libavcodec/avcodec.h>
#include <libavfilter/avfilter.h>
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
#include <libavformat/avformat.h>
#include <libavutil/avutil.h>
#include <libavutil/hwcontext.h>
#include <libavutil/imgutils.h>
#include <libavutil/opt.h>
#include <libavutil/pixdesc.h>
#include <libavutil/pixfmt.h>
#include <libswresample/swresample.h>
#include <libswscale/swscale.h>
#include <stdio.h>
AVCodecContext *h264_codec_ctx = NULL;
AVCodec *h264_codec = NULL;
static int enc_init(int width, int height, int fps)
{
char *codec_name = "h264_nvenc";
enum AVCodecID decodec_id = AV_CODEC_ID_H264;
// 列举支持的硬解码
enum AVHWDeviceType type = AV_HWDEVICE_TYPE_NONE;
printf("Available device types:\n");
while ((type = av_hwdevice_iterate_types(type)) != AV_HWDEVICE_TYPE_NONE) {
const char *type_name = av_hwdevice_get_type_name(type);
if (type_name) {
printf("%s\n", type_name);
} else {
printf("Unknown device type\n");
}
}
type = av_hwdevice_find_type_by_name("cuda");
if (type == AV_HWDEVICE_TYPE_NONE) {
printf("not support nvidia codec\n");
exit(0);
}
h264_codec = avcodec_find_encoder_by_name(codec_name);
if (!h264_codec) {
printf("not find codec\n");
exit(0);
}
h264_codec_ctx = avcodec_alloc_context3(h264_codec);
if (!h264_codec_ctx) {
printf("avcodec_alloc_context3 error\n");
exit(0);
}
h264_codec_ctx->codec_type = AVMEDIA_TYPE_VIDEO;
h264_codec_ctx->pix_fmt = AV_PIX_FMT_YUV420P;
h264_codec_ctx->width = width;
h264_codec_ctx->height = height;
h264_codec_ctx->time_base.num = 1;
h264_codec_ctx->time_base.den = fps;
h264_codec_ctx->bit_rate = 4000000;
h264_codec_ctx->gop_size = 2 * fps;
h264_codec_ctx->thread_count = 1;
h264_codec_ctx->slices = 1; // int slice_count; // slice数 int slices; // 切片数量。 表示图片细分的数量。 用于并行解码。
/**
* 遇到问题:编码得到的h264文件播放时提示"non-existing PPS 0 referenced"
* 分析原因:未将pps sps 等信息写入
* 解决方案:加入标记AV_CODEC_FLAG2_LOCAL_HEADER
*/
h264_codec_ctx->flags |= AV_CODEC_FLAG2_LOCAL_HEADER;
h264_codec_ctx->flags |= AV_CODEC_FLAG_LOW_DELAY;
// 不能使用下面的参数,否则硬编码器打不开
// AVDictionary *param = 0;
// priv_data 属于每个编码器特有的设置域,用av_opt_set 设置
// av_opt_set(h264_codec_ctx_->priv_data, "preset", "ultrafast", 0);
// av_opt_set(h264_codec_ctx_->priv_data, "tune", "zerolatency", 0);
// av_dict_set(¶m, "preset", "ultrafast", 0);
// av_dict_set(¶m, "profile", "baseline", 0);
if (avcodec_open2(h264_codec_ctx, h264_codec, NULL) < 0) {
printf("avcodec_open2 error\n");
exit(0);
}
printf("enc_init ok\n");
return 0;
}
static int enc_uninit()
{
avcodec_close(h264_codec_ctx);
avcodec_free_context(&h264_codec_ctx);
printf("enc_uninit ok\n");
return 0;
}
static enc_write(AVFrame *frame, int width, int height, FILE *fp_dst)
{
int ret = avcodec_send_frame(h264_codec_ctx, frame);
if (ret < 0) {
printf("Error sending a frame for encoding\n");
return -1;
}
AVPacket *packet = av_packet_alloc();
av_init_packet(packet);
packet->data = NULL;
packet->size = 0;
while (ret >= 0) {
ret = avcodec_receive_packet(h264_codec_ctx, packet);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
break;
} else if (ret < 0) {
printf("Error during encoding\n");
break;
}
fwrite(packet->data, packet->size, 1, fp_dst);
av_packet_unref(packet);
}
av_packet_free(&packet);
return 0;
}
int main(int argc, char **argv)
{
if (argc < 6) {
printf("./bin input(YUV420P) output width height fps\n");
return -1;
}
FILE *fp_src = fopen(argv[1], "rb");
FILE *fp_dst = fopen(argv[2], "wb");
if (fp_src == NULL || fp_dst == NULL) {
printf("Error opening files.\n");
return -1;
}
int width = atoi(argv[3]);
int height = atoi(argv[4]);
int fps = atoi(argv[5]);
enc_init(width, height, fps);
AVFrame *frame = av_frame_alloc();
if (!frame) {
printf("Error allocating AVFrame.\n");
return -1;
}
frame->width = width;
frame->height = height;
frame->format = AV_PIX_FMT_YUV420P;
// Allocate buffer for AVFrame data
int ret = av_frame_get_buffer(frame, 0);
if (ret < 0) {
printf("Error allocating buffer for AVFrame.\n");
return -1;
}
int num = 0;
while (1) {
// Y
if (fread(frame->data[0], width * height, 1, fp_src) != 1)
break;
// U
if (fread(frame->data[1], width * height / 4, 1, fp_src) != 1)
break;
// V
if (fread(frame->data[2], width * height / 4, 1, fp_src) != 1)
break;
num++;
printf("frame num:%d\n", num);
enc_write(frame, width, height, fp_dst);
}
// fflush
enc_write(NULL, width, height, fp_dst);
av_frame_free(&frame);
fclose(fp_src);
fclose(fp_dst);
enc_uninit();
return 0;
}
我的开源:
1、Nvidia视频硬解码、渲染、软/硬编码并写入MP4文件。项目地址:https://github.com/BreakingY/Nvidia-Video-Codec
2、Jetson Jetpack5.x视频编解码。项目地址:https://github.com/BreakingY/jetpack-dec-enc
3、音视频(H264/H265/AAC)封装、解封装、编解码pipeline,支持NVIDIA、昇腾DVPP硬编解码。项目地址:https://github.com/BreakingY/Media-Codec-Pipeline
4、simple rtsp server,小而高效的rtsp服务器,支持H264、H265、AAC、PCMA;支持TCP、UDP;支持鉴权。项目地址:https://github.com/BreakingY/simple-rtsp-server
5、simple rtsp client,rtsp客户端,支持TCP、UDP、H264、H265、AAC、PCMA,支持鉴权。项目地址:https://github.com/BreakingY/simple-rtsp-client
6、libflv,flv muxer/demuxer,支持H264/H265、AAC。项目地址:https://github.com/BreakingY/libflv
7、mpeg2 ts ps muxer/demuxer,支持H264/H265/MPEG1 audio/MP3/AAC/AAC_LATM/G711。项目地址:https://github.com/BreakingY/libmpeg2core
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库