首页 > 解决方案 > 如何使用 ffmpeg 将 h264 帧保存为 jpeg 图像?

问题描述

我想从 h264 流中保存缩略图,我将其转换为 ffmpeg avpackets 作为 jpeg。

我从 h264 AVPacket (iframe) 开始,并使用 avcodec_send_packet/avcodec_receive_frame 将其解码为 AVFrame。现在尝试使用 avcodec_send_frame/avcodec_receive_packet 从 AVFrame 转换为 AVPacket

我可以转换为 png 而不是 jpg,尽管我确实得到了一个视频,它看起来像是在输出三个单独的帧并排挤成一个。想知道它是不是一帧是R,下一个是G,最后是B。我不确定,显然我在那里做错了什么。我认为它可能是 png 编码器,我不需要它,所以让我们先让 jpg 工作。但是 jpg 正在输出无法打开的文件。

有什么建议吗?

这是我的代码:

int output_thumbnails(AVPacket* video_packet)
{
    char png_file_name[max_chars_per_filename];
    char thumbnail_id_char[10];
    _itoa_s(thumbnail_id, thumbnail_id_char, 10);
    strcpy_s(png_file_name, max_chars_per_filename, time_stamped_filepath);
    strcat_s(png_file_name, max_chars_per_filename, time_stamped_filename);
    strcat_s(png_file_name, max_chars_per_filename, thumbnail_id_char);
    strcat_s(png_file_name, max_chars_per_filename, ".jpg");
    thumbnail_id++;

    int error_code = send_AVPacket_to_videocard(video_packet, av_codec_context_RTSP);

    //if (error_code == AVERROR_EOF)
    //{
    //  //  error_code = videocard_to_PNG(png_file_name, av_codec_context_RTSP, av_codec_RTSP);
    //}
    if (error_code == AVERROR(EAGAIN)) //send packets to videocard until function returns EAGAIN
    {
        error_code = videocard_to_PNG(png_file_name, av_codec_context_RTSP);

        //EAGAIN means that the video card buffer is ready to have the png pulled off of it
        if (error_code == AVERROR_EOF)
        {
            //  error_code = videocard_to_PNG(png_file_name, av_codec_context_RTSP, av_codec_RTSP);
        }
        else if (error_code == AVERROR(EAGAIN))
        {

        }
        else
        {
            deal_with_av_errors(error_code, __LINE__, __FILE__);
        }
    }
    else
    {
        deal_with_av_errors(error_code, __LINE__, __FILE__);
    }
    
    return 0;
}

VideoThumbnailGenerator.h: #include "VideoThumbnailGenerator.h"

bool decoder_context_created = false;
bool encoder_context_created = false;

AVCodecContext* h264_decoder_codec_ctx;
AVCodecContext* thumbnail_encoder_codec_ctx;

int send_AVPacket_to_videocard(AVPacket* packet, AVCodecContext* codec_ctx)
{
    if(!decoder_context_created)
    {
        AVCodec* h264_codec = avcodec_find_decoder(codec_ctx->codec_id);
        h264_decoder_codec_ctx = avcodec_alloc_context3(h264_codec);

        h264_decoder_codec_ctx->width = codec_ctx->width;
        h264_decoder_codec_ctx->height = codec_ctx->height;
        h264_decoder_codec_ctx->pix_fmt = AV_PIX_FMT_RGB24;
        h264_decoder_codec_ctx->codec_type = AVMEDIA_TYPE_VIDEO;
        h264_decoder_codec_ctx->skip_frame = AVDISCARD_NONINTRA;//AVDISCARD_NONREF;//AVDISCARD_NONINTRA;
        
        h264_decoder_codec_ctx->time_base.num = 1;
        h264_decoder_codec_ctx->time_base.den = 30;

        h264_decoder_codec_ctx->extradata = codec_ctx->extradata;
        h264_decoder_codec_ctx->extradata_size = codec_ctx->extradata_size;
        
        int error_code = avcodec_open2(h264_decoder_codec_ctx, h264_codec, NULL);
        if (!h264_codec) {
            return -1;
        }

        if (error_code < 0)
        {
            return error_code;
        }
        decoder_context_created = true;
    }

    
    //use hardware decoding to decode video frame
    int error_code = avcodec_send_packet(h264_decoder_codec_ctx, packet);
    if(error_code == AVERROR(EAGAIN))
    {
        return AVERROR(EAGAIN);
    }
    if(error_code<0)
    {
        printf("Error: Could not send packet to video card");
        return error_code;
    }

    return 0;
}

int videocard_to_PNG(char *png_file_path, AVCodecContext* codec_ctx)
{
    if (!encoder_context_created)
    {
        //AVCodec* thumbnail_codec = avcodec_find_encoder(AV_CODEC_ID_PNG);
        AVCodec* thumbnail_codec = avcodec_find_encoder(AV_CODEC_ID_JPEG2000);
        thumbnail_encoder_codec_ctx = avcodec_alloc_context3(thumbnail_codec);

        thumbnail_encoder_codec_ctx->width = 128;
        thumbnail_encoder_codec_ctx->height = (int)(((float)codec_ctx->height/(float)codec_ctx->width) * 128);
        thumbnail_encoder_codec_ctx->pix_fmt = AV_PIX_FMT_RGB24; //AV_PIX_FMT_YUVJ420P
        thumbnail_encoder_codec_ctx->codec_type = AVMEDIA_TYPE_VIDEO;

        thumbnail_encoder_codec_ctx->time_base.num = 1;
        thumbnail_encoder_codec_ctx->time_base.den = 30;

        bool thread_check = thumbnail_encoder_codec_ctx->thread_type & FF_THREAD_FRAME;
        bool frame_threads_check = thumbnail_encoder_codec_ctx->codec->capabilities & AV_CODEC_CAP_FRAME_THREADS;
        
        int error_code = avcodec_open2(thumbnail_encoder_codec_ctx, thumbnail_codec, NULL);
        if (!thumbnail_codec) {
            return -1;
        }

        if (error_code < 0)
        {
            return error_code;
        }
        encoder_context_created = true;
    }
    
    AVFrame* thumbnail_frame = av_frame_alloc();
    AVPacket* thumbnail_packet = av_packet_alloc();
    //av_init_packet(png_packet);
    int error_code = avcodec_receive_frame(h264_decoder_codec_ctx, thumbnail_frame);

    //check for errors everytime
    //note EAGAIN errors won't get here since they won't get past while
    if (error_code < 0 && error_code != AVERROR(EAGAIN))
    {
        printf("Error: Could not get frame from video card");
        return error_code;
    }
    //empty buffer if there are any more frames to pull (there shouldn't be)
    //while(error_code != AVERROR(EAGAIN))
    //{
    //  //check for errors everytime
    //  //note EAGAIN errors won't get here since they won't get past while
    //  if (error_code < 0)
    //  {
    //      printf("Error: Could not get frame from video card");
    //      return error_code;
    //  }
    //  
    //  error_code = avcodec_receive_frame(h264_decoder_codec_ctx, png_frame);
    //}

    //now we convert back to AVPacket, this time one holding PNG info, so we can store to file
    error_code = avcodec_send_frame(thumbnail_encoder_codec_ctx, thumbnail_frame);

    if (error_code >= 0) {
        error_code = avcodec_receive_packet(thumbnail_encoder_codec_ctx, thumbnail_packet);

        FILE* out_PNG;

        errno_t err = fopen_s(&out_PNG, png_file_path, "wb");
        if (err == 0) {
            fwrite(thumbnail_packet->data, thumbnail_packet->size, 1, out_PNG);
        }
        fclose(out_PNG);

    }
    return error_code;
}

标签: c++ffmpegjpegh.264

解决方案


推荐阅读