MrEinsa
MrEinsa

Reputation: 33

FFMPEG H264 encode each single image

i encode currently a QImage from RGB888 to H264, but i want to encode each image (even if this is not the perfect way) by itself. Im able to encode the image, but its needed to send the same image 46 times. And i dont know what i do wrong (probably wrong config of the encode, but i cannot find the issue there).

Afterwards i decode this image and then convert it back to a QImage. I do this only for testing some other code.

avcodec_register_all();

AVCodec *nVidiaCodec = avcodec_find_encoder_by_name("h264_nvenc");
if (!nVidiaCodec)
{
    return false;
}

AVCodecContext* av_codec_context_ = NULL;
av_codec_context_ = avcodec_alloc_context3(nVidiaCodec);
if (!av_codec_context_)
{
    return false;
}

av_codec_context_->width = dst->width;
av_codec_context_->height = dst->height;
av_codec_context_->pix_fmt = AV_PIX_FMT_YUV420P;
av_codec_context_->gop_size = 1;
av_codec_context_->keyint_min = 0;
av_codec_context_->scenechange_threshold = 0;
av_codec_context_->bit_rate = 8000000;
av_codec_context_->time_base.den = 1;
av_codec_context_->time_base.num = 1;
av_codec_context_->refs = 0;
av_codec_context_->qmin = 1;
av_codec_context_->qmax = 1;
av_codec_context_->b_frame_strategy = 0;
av_codec_context_->max_b_frames = 0;
av_codec_context_->thread_count = 1;

av_opt_set(av_codec_context_, "preset", "slow", 0);
av_opt_set(av_codec_context_, "tune", "zerolatency", 0);

int ret = avcodec_open2(av_codec_context_, nVidiaCodec, NULL);
if (0 > ret)
{
    return false;
}

AVFrame *picture = av_frame_alloc();
picture->format = AV_PIX_FMT_RGB24;
picture->width = dst->width;
picture->height = dst->height;

ret = avpicture_fill((AVPicture *)picture, imgSrc.bits(), AV_PIX_FMT_RGB24, dst->width, dst->height);
if (0 > ret)
{
    return false;
}

AVFrame *tmp_picture = av_frame_alloc();
tmp_picture->format = AV_PIX_FMT_YUV420P;
tmp_picture->width = dst->width;
tmp_picture->height = dst->height;
ret = av_frame_get_buffer(tmp_picture, 32);

SwsContext *img_convert_ctx = sws_getContext(av_codec_context_->width, av_codec_context_->height, AV_PIX_FMT_RGB24, av_codec_context_->width, av_codec_context_->height, av_codec_context_->pix_fmt, SWS_BICUBIC, NULL, NULL, NULL);
if (!img_convert_ctx)
{
    return false;
}
ret = sws_scale(img_convert_ctx, picture->data, picture->linesize, 0, av_codec_context_->height, tmp_picture->data, tmp_picture->linesize);
if (0 > ret)
{
    return  false;
}

ret = avcodec_send_frame(av_codec_context_, tmp_picture);
if (0 > ret)
{
    return  false;
}

AVPacket pkt;
av_init_packet(&pkt);
pkt.data = NULL;
pkt.size = 0;
do 
{
    ret = avcodec_receive_packet(av_codec_context_, &pkt);
    if (ret == 0)
    {
        break;
    }
    else if ((ret < 0) && (ret != AVERROR(EAGAIN)))
    {
        return false;
    }
    else if (ret == AVERROR(EAGAIN))
    {
        ret = avcodec_send_frame(av_codec_context_, tmp_picture);
        if (0 > ret)
        {
            return false;
        }
    }
} while (ret == 0);

// the do while is called 46 times, then i get the packet, but i want to get the packet at the first call

It would be very nice if you can help me.

Thanks guys.

Upvotes: 3

Views: 1322

Answers (1)

Markus Schumann
Markus Schumann

Reputation: 8254

I assume you just want to encode a single frame. You need to flush the encoder after you have sent your single uncompressed frame by sending NULL instead of a valid buffer.

int result = 0;

// encoder init


// send one uncompressed frame
result = avcodec_send_frame(av_codec_context_, tmp_picture);

if (result < 0) return false;

// send NULL to indicate flushing
result = avcodec_send_frame(av_codec_context_, NULL);


if (result < 0) return false;


while (result != AVERROR_EOF)
{
    result = avcodec_receive_packet(av_codec_context_, &pkt);

    if (!result)
    {
        // you should have your encoded frame; do something with it
    }
}

Upvotes: 3

Related Questions