FFMPEG H264对每个图像进行编码

FFMPEG H264 encode each single image

本文关键字:编码 图像 H264 FFMPEG      更新时间:2023-10-16

我目前将QImage从RGB888编码为H264,但我想单独编码每个图像(即使这不是完美的方式(。我能够对图像进行编码,但它需要发送相同的图像 46 次。而且我不知道我做错了什么(可能是错误的编码配置,但我在那里找不到问题(。

之后,我解码了这个图像,然后将其转换回QImage。我这样做只是为了测试其他一些代码。

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

如果你能帮助我,那就太好了。

谢谢大家。

我假设你只想对单个帧进行编码。在发送单个未压缩帧后,需要通过发送 NULL 而不是有效缓冲区来刷新编码器。

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
    }
}