简体   繁体   English

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

[英]How to encode 1 image and receive it using ffmpeg h264_nvenc

I am trying to create a real-time stream of my screen using ffmpeg and encoder h264_nvenc but there's 5-6 frames delay between my mouse move and mouse move on the translation(first 5-6 frames are black)我正在尝试使用 ffmpeg 和编码器 h264_nvenc 创建我的屏幕的实时 stream 但在我的鼠标移动和鼠标移动之间有 5-6 帧延迟(前 5-6 帧是黑色的)

I need to somehow encode 1 image of my screen using h264_nvenc and receive it as valid frame(packet) or maybe decrease delay to 1-2 frames我需要以某种方式使用 h264_nvenc 对我的屏幕的 1 个图像进行编码并将其作为有效帧(数据包)接收,或者可能将延迟减少到 1-2 帧

Maybe there are some other methods to get the image right away也许还有其他一些方法可以立即获取图像

UPD: I realised that my latency is because of encoder and decoder frames delay so is there any way to completely remove this frame delays?(Added decoder init function and decode function) UPD:我意识到我的延迟是因为编码器和解码器帧延迟所以有没有办法完全消除这个帧延迟?(添加解码器初始化 function 和解码功能)

I've done some tests and managed to receive image right away with decoder and encoder flush method but cant do this the second time我已经做了一些测试,并设法使用解码器和编码器刷新方法立即接收图像,但第二次无法这样做

Encode function:编码 function:

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 encoding\n");
        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: %i\n", count, size);
            return;
        }
        else if (ret < 0) {
            fprintf(stderr, "Error during encoding\n");
            exit(1);
        }
        size += pkt->size;

        *new_packet = av_packet_clone(pkt);
        
        av_packet_unref(pkt);
    }
}

FFMpeg encoder initialization: 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 found\n", "hevc");
        exit(1);
    }

    /*codec = avcodec_find_encoder(AV_CODEC_ID_H265);
    if (!codec) {
        fprintf(stderr, "Codec '%s' not found\n", "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 context\n");
        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: %s\n", 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 frame\n");
        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 data\n");
        exit(1);
    }

    cout << "7" << endl;
}

Decoder init function:解码器初始化 function:

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 found\n");
        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);
    }
}

Decode function:解码 function:

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 decoding\n");
        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 decoding\n");
            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;
    }
}

You can check av_seek_frame(av_format_ctx, video_stream_index, timestamp, AVSEEK_FLAG_BACKWARD) out for your decoder.您可以为您的解码器检查av_seek_frame(av_format_ctx, video_stream_index, timestamp, AVSEEK_FLAG_BACKWARD) Go to the frame you want to encode then encode that frame. Go 到要编码的帧,然后对该帧进行编码。

Encoding a single frame works just like in the loop after you seek to the frame you want to seek, note that av_seek seeks iFrames.对单个帧进行编码就像在您查找要查找的帧之后在循环中一样工作,请注意 av_seek 查找 iFrame。

For h264_nvenc set option delay to 0对于 h264_nvenc 将选项delay设置为0
But for decoder h264_cuvid I don't know how to make 0 delay stream so I switched to DXVA2 and set max_b_frames to 0但是对于解码器h264_cuvid我不知道如何使 0 延迟 stream 所以我切换到 DXVA2 并将max_b_frames设置为0

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM