如何编码1个图像并使用ffmpeg h264_nvenc接收它

How to encode 1 image and receive it using ffmpeg h264_nvenc

本文关键字:h264 ffmpeg nvenc 何编码 编码 图像 1个      更新时间:2024-09-29

我正试图使用ffmpeg和编码器h264_nvenc创建屏幕的实时流,但在平移时,我的鼠标移动和鼠标移动之间有5-6帧延迟(前5-6帧为黑色(

我需要以某种方式使用h264_nvenc对我的屏幕的1个图像进行编码,并将其作为有效帧(包(接收,或者可能将延迟减少到1-2帧

也许还有一些其他方法可以立即获得图像

UPD:我意识到我的延迟是因为编码器和解码器的帧延迟,所以有什么方法可以完全消除这种帧延迟吗?(增加了解码器初始化功能和解码功能(

我已经做了一些测试,并设法用解码器和编码器冲洗方法立即接收图像,但第二次无法做到这一点

编码功能:

static void encode(AVCodecContext* enc_ctx, AVFrame* frame, AVPacket* pkt, AVPacket** new_packet)
{
int ret;
ret = avcodec_send_frame(enc_ctx, frame);
if (ret < 0) {
fprintf(stderr, "Error sending a frame for encodingn");
exit(1);
}
int size = 0;
while (ret >= 0) {
ret = avcodec_receive_packet(enc_ctx, pkt);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
{
//printf("count: %i size: %in", count, size);
return;
}
else if (ret < 0) {
fprintf(stderr, "Error during encodingn");
exit(1);
}
size += pkt->size;
*new_packet = av_packet_clone(pkt);

av_packet_unref(pkt);
}
}

FFMpeg编码器初始化:

void InitializeFFmpegEncoder()
{
const AVCodec* codec;
int i, ret, x, y;
FILE* f;
// Encoder
codec = avcodec_find_encoder_by_name("h264_nvenc"); // hevc_nvenc h264_nvenc
if (!codec) {
fprintf(stderr, "Codec '%s' not foundn", "hevc");
exit(1);
}
/*codec = avcodec_find_encoder(AV_CODEC_ID_H265);
if (!codec) {
fprintf(stderr, "Codec '%s' not foundn", "hevc");
exit(1);
}*/
cout << "Encoder codec name: " << codec->name << endl;
cout << "1" << endl;
enc_c = avcodec_alloc_context3(codec);
if (!enc_c) {
fprintf(stderr, "Could not allocate video codec contextn");
exit(1);
}
cout << "2" << endl;
pkt = av_packet_alloc();
if (!pkt)
exit(1);
cout << "3" << endl;
/* put sample parameters */
enc_c->bit_rate = 192000000;
/* resolution must be a multiple of two */
enc_c->width = 1920;
enc_c->height = 1080;
/* frames per second */
enc_c->time_base = AVRational(1, 1);
//enc_c->framerate = AVRational(60, 1);
/* emit one intra frame every ten frames
* check frame pict_type before passing frame
* to encoder, if frame->pict_type is AV_PICTURE_TYPE_I
* then gop_size is ignored and the output of encoder
* will always be I frame irrespective to gop_size
*/
enc_c->gop_size = 1;
enc_c->max_b_frames = 0;
enc_c->pix_fmt = AV_PIX_FMT_BGR0;
enc_c->keyint_min = 0;
if (codec->id == AV_CODEC_ID_H264)
{
//av_opt_set(enc_c->priv_data, "preset", "p1", 0);
//av_opt_set(enc_c->priv_data, "tune", "ull", 0);
//av_opt_set(enc_c->priv_data, "zerolatency", "1", 0);
//av_opt_set(enc_c->priv_data, "preset", "p1", 0);
//av_opt_set(enc_c->priv_data, "tune", "ull", 0);
//av_opt_set(enc_c->priv_data, "strict_gop", "1", 0);
//av_opt_set(enc_c->priv_data, "preset", "lossless", 0);
//av_opt_set(enc_c->priv_data, "zerolatency", "1", 0);
}
cout << "4" << endl;
/* open it */
ret = avcodec_open2(enc_c, codec, NULL);
if (ret < 0) {
printf("Could not open codec: %sn", av_make_error_string((char[64])(0), 64, ret));
exit(1);
}
cout << "5" << endl;
frame = av_frame_alloc();
if (!frame) {
fprintf(stderr, "Could not allocate video framen");
exit(1);
}
frame->format = AV_PIX_FMT_BGR0; // AV_PIX_FMT_YUV444P AV_PIX_FMT_ARGB
frame->width = enc_c->width;
frame->height = enc_c->height;
cout << "6" << endl;
ret = av_frame_get_buffer(frame, 0);
if (ret < 0) {
fprintf(stderr, "Could not allocate the video frame datan");
exit(1);
}
cout << "7" << endl;
}

解码器初始化功能:

int InitializeFFmpegDecoder()
{
int ret;
const AVCodec* decoder = NULL;
enum AVHWDeviceType type;
int i;
decoder = avcodec_find_decoder_by_name("h264_cuvid"); // h264_cuvid hevc_cuvid
if (!decoder) {
fprintf(stderr, "Codec not foundn");
exit(1);
}
cout << "Decoder name: " << decoder->name << endl;
if (!(decoder_ctx = avcodec_alloc_context3(decoder)))
return AVERROR(ENOMEM);
decoder_ctx->get_format = ffmpeg_GetFormat;
decoder_ctx->gop_size = 0;
decoder_ctx->max_b_frames = 0;
decoder_ctx->keyint_min = 0;
decoder_ctx->flags |= AV_CODEC_FLAG_LOW_DELAY;
//decoder_ctx->get_format = get_format;
if ((ret = avcodec_open2(decoder_ctx, decoder, NULL)) < 0) {
fprintf(stderr, "Failed to open codec for stream n");
return -1;
}
if (ctx == NULL)
{
ctx = sws_getContext(1920, 1080,
AV_PIX_FMT_NV12, 1920, 1080, // AV_PIX_FMT_YUV420P AV_PIX_FMT_NV12
AV_PIX_FMT_BGR0, 0, 0, 0, 0);
}
}

解码功能:

static void decode(AVCodecContext* dec_ctx, AVFrame* frame, AVPacket* pkt, char* bgra_image)
{
int ret;
ret = avcodec_send_packet(dec_ctx, pkt);
if (ret < 0) {
fprintf(stderr, "Error sending a packet for decodingn");
exit(1);
}
while (ret >= 0) {
ret = avcodec_receive_frame(dec_ctx, frame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
return;
else if (ret < 0) {
fprintf(stderr, "Error during decodingn");
exit(1);
}
char* outData[1] = { bgra_image }; // RGB24 have one plane
int outLinesize[1] = { 4 * 1920 }; // RGB stride
sws_scale(ctx, frame->data, frame->linesize, 0, 1080, (uint8_t* const*)outData, outLinesize);
//*new_frame = av_frame_clone(frame);
//av_frame_ref()
//break;
}
}

您可以为解码器检查av_seek_frame(av_format_ctx, video_stream_index, timestamp, AVSEEK_FLAG_BACKWARD)。转到要编码的帧,然后对该帧进行编码。

对单个帧进行编码的工作方式与查找要查找的帧后的循环中的工作方式相同,请注意av_seek查找iFrames。

对于h264_nvenc,将选项delay设置为0
但是对于解码器h264_cuvid,我不知道如何制作0延迟流,所以我切换到DXVA2并将max_b_frames设置为0