C++ 如何使用ffmpeg将h264帧保存为jpeg图像?

C++ 如何使用ffmpeg将h264帧保存为jpeg图像?,c++,ffmpeg,jpeg,h.264,C++,Ffmpeg,Jpeg,H.264,我想将h264流中的缩略图保存为JPEG格式的ffmpeg AV数据包 我从一个h264 AVPacket(iframe)开始,使用avcodec\u send\u packet/avcodec\u receive\u frame将其解码为一个AVFrame。现在尝试使用avcodec\u发送\u帧/avcodec\u接收\u数据包从AVFrame转换为AVPacket 我可以转换成png而不是jpg,虽然我确实得到了一个视频,看起来像是将三个单独的帧并排压缩成一个。不知道是不是一帧是R,下一帧

我想将h264流中的缩略图保存为JPEG格式的ffmpeg AV数据包

我从一个h264 AVPacket(iframe)开始,使用avcodec\u send\u packet/avcodec\u receive\u frame将其解码为一个AVFrame。现在尝试使用avcodec\u发送\u帧/avcodec\u接收\u数据包从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: #包括“VideoThumbnailGenerator.h”

bool解码器\u上下文\u创建=false;
bool编码器\u上下文\u创建=false;
AVCodecContext*h264_解码器_编解码器_ctx;
AvcodeContext*缩略图\编码器\编解码器\ ctx;
int发送AVPacket到视频卡(AVPacket*数据包,AVCodecContext*编解码器ctx)
{
如果(!解码器\u上下文\u已创建)
{
AVCodec*h264\u codec=AVCodec\u find\u解码器(codec\u ctx->codec\u id);
h264_解码器_编解码器_ctx=avcodec_alloc_上下文3(h264_编解码器);
h264\u解码器\u编解码器\u ctx->宽度=编解码器\u ctx->宽度;
h264\u解码器\u编解码器\u ctx->高度=编解码器\u ctx->高度;
h264_解码器_编解码器_ctx->pix_fmt=AV_pix_fmt_RGB24;
h264\解码器\编解码器\ ctx->编解码器\类型=AVMEDIA \类型\视频;
h264_解码器_编解码器_ctx->skip_frame=AVDISCARD_nontrans;//AVDISCARD_NONREF;//AVDISCARD_nontrans;
h264_解码器_编解码器_ctx->time_base.num=1;
h264_解码器_编解码器_ctx->time_base.den=30;
h264\u解码器\u编解码器\u ctx->extradata=编解码器\u ctx->extradata;
h264_解码器_编解码器_ctx->extradata_size=codec_ctx->extradata_size;
int error\u code=avcodec\u open2(h264\u解码器\u编解码器\u ctx,h264\u编解码器,空);
如果(!h264_编解码器){
返回-1;
}
如果(错误代码<0)
{
返回错误代码;
}
解码器\u上下文\u已创建=真;
}
//使用硬件解码来解码视频帧
int error_code=avcodec_send_数据包(h264_解码器_codec_ctx,数据包);
如果(错误代码==平均错误(EAGAIN))
{
返回平均值(EAGAIN);
}
如果(错误_codewidth=128;
缩略图编码器编解码器高度=(int)((浮点)编解码器高度/(浮点)编解码器宽度)*128);
缩略图\u编码器\u编解码器\u ctx->pix\u fmt=AV\u pix\u fmt\u RGB24;//AV\u pix\u fmt\u YUVJ420P
缩略图\u编码器\u编解码器\u ctx->codec\u type=AVMEDIA\u type\u视频;
缩略图\u编码器\u编解码器\u ctx->time\u base.num=1;
缩略图\u编码器\u编解码器\u ctx->time\u base.den=30;
bool thread\u check=缩略图\u编码器\u编解码器\u ctx->线程类型和FF\u线程\u帧;
bool frame\u threads\u check=缩略图\u编码器\u编解码器\u ctx->codec->功能和AV\u编解码器\u CAP\u frame\u线程;
int error\u code=avcodec\u open2(缩略图\u编码器\u编解码器\u ctx,缩略图\u编解码器,NULL);
如果(!缩略图_编解码器){
返回-1;
}
如果(错误代码<0)
{
返回错误代码;
}
编码器\u上下文\u已创建=真;
}
AVFrame*thumbnail_frame=av_frame_alloc();
AVPacket*缩略图_packet=av_packet_alloc();
//av_初始_包(png_包);
int error_code=avcodec_接收_帧(h264_解码器_codec_ctx,缩略图_帧);
//每次都检查错误
//注意:EAGAIN错误不会出现在这里,因为它们不会在
if(错误代码<0&&error\u代码!=AVERROR(EAGAIN))
{
printf(“错误:无法从视频卡获取帧”);
返回错误代码;
}
//如果还有更多帧要拉,则清空缓冲区(不应该有)
//while(错误代码!=averor(EAGAIN))
//{
////每次都检查错误
////注意:EAGAIN错误不会出现在这里,因为它们不会在
//如果(错误代码<0)
//  {
//printf(“错误:无法从视频卡获取帧”);
//返回错误代码;
//  }
//  
//错误代码=avcodec接收帧(h264解码器ctx,png帧);
//}
//现在我们转换回AVPacket,这次是一个保存PNG信息的包,所以我们可以存储到文件中
错误代码=avcodec发送帧(缩略图编码器ctx,缩略图帧);
如果(错误代码>=0){
错误代码=avcodec接收数据包(缩略图编码器ctx,缩略图数据包);
文件*out\u PNG;
errno_t err=fopen_s(&out_PNG,PNG_file_path,“wb”);
如果(错误==0){
写入(缩略图数据包->数据,缩略图数据包->大小,1,输出PNG);
}
fclose(out_PNG);
}
返回错误代码;
}

<代码> >代码> STRCAT/CODE>,这是一个令人担忧的级别,它被称为C++代码。为什么不<代码> STD::String ?也许是一个好主意,切换到String,只是读一下关于STRCAT性能问题。我倾向于只按需要来坚持C函数和C++,但这看起来是个不错的选择。谢谢。这不是性能问题,而是Eclipse。使用固定大小的缓冲区会带来缓冲区溢出或内存泄漏的风险。使用strcat_会在缓冲区溢出/内存泄漏方面有所帮助。固定大小的缓冲区删除..会很好,因为这个程序的性能是第一位的。谢谢tadman。目前更担心的是如何将这个jpg正确保存到文件中这是一个令人担忧的级别,代码< >代码> >代码>为什么C++不>代码> STD::String ?也许是一个好主意,切换到String,只是读一下关于STRCAT的性能问题。我只需要按C函数和C++来做,但是这看起来是个不错的选择。谢谢。这不是性能问题,它是删除。避免使用固定大小的缓冲区,并冒缓冲区溢出或内存泄漏的风险
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;
}