sjwouc
2021-07-20 16:25
采纳率: 0%
浏览 54

使用ffmpeg动态库将mp4转为gif

问题概述:MP4文件转GIF文件的时候,av_receive_packet返回EAGAIN的提示,按api的注释我应该再次send frame操作,但是再次send frame后程序异常。

大致过程:

(1)、初始化输入文件(MP4)
(2)、初始化输出文件(GIF):pix_format使用AV_PIX_FMT_BGR8,编码器使用AV_CODEC_ID_GIF
(3)、初始化swsContext(输入文件格式(YUV420)等宽高转BGR8数据)
(4)、初始化数据缓冲区和编码使用的AVFrame,还有解码编码使用的packet
(5)、开始进行转换操作

*pCodecCtx_inFile:输入文件对应的AVCodecContext*
*pCodecCtx_outFile:输出文件对应的AVCodecContext*
*pFrameGif:编码使用的AVFrame*
*packet_In解码时用的packet结构*
*pFormatCtx_In 输入文件的AVFormatContext结构*
*pFormatCtx_Out 输出文件的AVFormatContext结构*
*pSwsContext 格式转换结构*
*videoIndex 输入文件的流媒体中视频流的index*
*vecFrame 存储解码帧的数组*

#define FPS 30

pSwsContext = sws_getContext(pCodecCtx_inFile->width, pCodecCtx_inFile->height,  pCodecCtx->pix_fmt, pCodecCtx_inFile->width, pCodecCtx_inFile->height, AV_PIX_FMT_BGR8, SWS_BICUBIC, NULL, NULL, NULL);

unsigned char* out_bufGif = (uint8_t*)malloc(av_image_get_buffer_size(AV_PIX_FMT_BGR8,  pCodecCtx_inFile->width, pCodecCtx_inFile->height, 1));

pFrameGif = av_frame_alloc();
pFrameGif->width = pCodecCtx_inFile->width;
pFrameGif->height = pCodecCtx_inFile->height;
pFrameGif->format = pCodecCtx_inFile->pix_fmt;
av_image_fill_arrays(pFrameGif->data, pFrameGif->linesize, out_bufGif, AV_PIX_FMT_BGR8, pCodecCtx_inFile->width, pCodecCtx_inFile->height, 1);

av_init_packet(&packet_In);

AVFrame *pFrame  = av_frame_alloc();
while (av_read_frame(pFormatCtx_In, &packet_In))
{
        if (packet_In.stream_index == videoIndex)
        {
                int ret = avcodec_send_packet(pCodecCtx_inFile, &packet_In);
                int got_picture = avcodec_receive_frame(pCodecCtx_inFile, pFrame);

                if (got_picture == 0)
                {
                        vecFrame.push_back(pFrame);
                }
        }

        av_packet_unref(&packet_In);
}

AVPacket pkt;
av_init_packet(&pkt);

int size = vecFrame.size();
for (int i = 0; i < size; i++)
{
        sws_scale(pSwsContext, (const uint8_t* const*)vecFrame[i]->data, vecFrame[i]->linesize, 0, pCodecCtx_inFile->height, pFrameGif->data, pFrameGif->linesize);

        pFrameGif->pts = i * ((pFormatCtx_In->streams[videoIndex]->time_base.den / pFormatCtx_In->streams[videoIndex]->time_base.num) / FPS);

        int ret = avcodec_send_frame(pCodecCtx_outFile, pFrameGif);

        if (ret < 0)
        {
                continue;
        }

        int rc = 0;
        while (true)
        {
                rc  = avcodec_receive_packet(pCodecCtx_outFile, &pkt);
                if (AVERROR(EAGAIN) == rc || AVERROR_EOF == rc)
                {
                        break;
                }
                if (rc < 0)
                {
                        return;
                }
                pkt.stream_index = 0;
                pkt.pts = av_rescale_q_rnd(pkt.pts, pFormatCtx_In->streams[videoIndex]->time_base, pFormatCtx_Out->streams[0]->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
                pkt.dts = av_rescale_q_rnd(pkt.dts, pFormatCtx_In->streams[videoIndex]->time_base, pFormatCtx_Out->streams[0]->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
                pkt.duration = ((pFormatCtx_Out->streams[videoIndex]->time_base.den / pFormatCtx_Out->streams[videoIndex]->time_base.num) / FPS);

                av_interleaved_write_frame(pFormatCtx_Out, &pkt);
        }

        delete[] out_bufGif;

        av_write_trailer(pFormatCtx_Out);

        avio_close(pFormatCtx_Out->pb);
}


问题表现:

rc = avcodec_receive_packet(pCodecCtx_outFile, &pkt);第一次执行的时候返回EAGAIN,第二次执行到avcodec_send_frame(pCodecCtx_outFile, pFrameGif)进行填充的时候会在avcodec-58.dll动态库里面报异常

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 邀请回答

2条回答 默认 最新

  • 有问必答小助手 2021-07-21 13:41

    你好,我是有问必答小助手,非常抱歉,本次您提出的有问必答问题,技术专家团超时未为您做出解答

    本次提问扣除的有问必答次数,将会以问答VIP体验卡(1次有问必答机会、商城购买实体图书享受95折优惠)的形式为您补发到账户。

    ​​​​因为有问必答VIP体验卡有效期仅有1天,您在需要使用的时候【私信】联系我,我会为您补发。

    点赞 评论
  • sjwouc 2021-07-21 16:40

    已经调试改好了:
    首先这句pFrameGif->format = pCodecCtx_inFile->pix_fmt粗心写错了,应该是gif的format AV_PIX_FMT_BGR8。

    调试的时候改动了代码需要改回来:
    解码帧AVFrame *pFrame = av_frame_alloc()的初始化应该在while循环里面,否则所有的帧都是一个内存空间,vector里面实际上只保存了一帧源数据。

    不知道为什么之前写的不包含这两个错误的代码也成功不了,可能是因为加了EAGAIN错误的判断处理吧,不够读的时候要继续去send操作。

    点赞 评论

相关推荐 更多相似问题