Why do I get a crash only sometimes when closing input file with ffmpeg

c++, ffmpeg

Solution

It looks to me like you need to move your call to `av_free_packet(&packet)` inside your while loop. So you currently have:

while(av_read_frame(pFormatCtx, &packet)>=0)
{
     // A bunch of operations here
}
// this is not the right place for this
av_free_packet(&packet);

Instead you should have

while(av_read_frame(pFormatCtx, &packet)>=0)
{
     // A bunch of operations here

    // this needs to be called for every call to av_read_frame()
    // so it must be inside the while loop
    av_free_packet(&packet);
}

See here for further details.

Considering that you writing this in C++ you could consider creating some simple RAII wrappers around these resources to make your resource management much easier.

EDIT

Based on your feedback it seems that this change that I recommended didn't do it. So I revisited the code. I implemented my own save to Jpeg as I could not see yours - though for test purposes it simply keeps over-writing the same file.

In order to simplify the code and get a handle on the resource management I implemented some "smart pointers" for the ffmpeg resources. These automatically clean up at scope exit. I cannot see a resource leak when running through this code and it correctly generates each frame as a jpeg file.

See if you get any value out of this:

extern "C" {
#include <libavdevice\avdevice.h>
#include <libavformat\avformat.h>
#include <libavfilter\avfilter.h>
#include <libavcodec\avcodec.h>
#include <libswscale\swscale.h>
}
#include <iostream>
#include <fstream>
#include <ios>
#include <type_traits>

template<typename T, typename D>
class AVHandle
{
    T *val;
    typedef D* deleter_t;
    deleter_t deleter;

    // not default constructible
    AVHandle();
    // non copiable
    AVHandle(const AVHandle&);
    AVHandle& operator=(const AVHandle&);
public:
    AVHandle(T *in, deleter_t del) : val(in), deleter(del)
    {}

    operator T *()
    {
        return val;
    }

    T* operator->()
    {
        return val;
    }

    bool is_valid()
    {
        return val != 0;
    }

    ~AVHandle()
    {
        deleter(val);
    }
};
typedef AVHandle<AVFrame, void (void*)> FrameHandle;
typedef AVHandle<AVCodecContext, int (AVCodecContext*)> CodecContextHandle;
typedef AVHandle<uint8_t, void(void*)> BufferHandle;

class AVFormatContextHandle
{
    AVFormatContext *val;

    // not default constrcutible
    AVFormatContextHandle();
    // non copiable
    AVFormatContextHandle(const AVFormatContextHandle&);
    AVFormatContextHandle& operator=(const AVFormatContextHandle&);
public:
    AVFormatContextHandle(AVFormatContext *ctx) : val(ctx)
    {}

    operator AVFormatContext *()
    {
        return val;
    }

    AVFormatContext* operator ->()
    {
        return val;
    }

    AVFormatContext** operator&()
    {
        return &val;
    }

    bool is_valid()
    {
        return val != 0;
    }

    ~AVFormatContextHandle()
    {
        if(val)
            avformat_close_input(&val);
    }
};

int free_context(AVCodecContext* c)
{
    int ret = avcodec_close(c);
    av_free(c);
    return ret;
}

bool PullFrame(const std::string& capturedUrl)
{
    AVCodec* pMJPEGCodec  = avcodec_find_encoder(CODEC_ID_MJPEG );

    int videoStream   = -1;

    AVDictionary *optionsDict = NULL;
    AVInputFormat   *pFormat = NULL;
    const char      formatName[] = "mp4";

    if (!(pFormat = av_find_input_format(formatName))) 
    {
        std::cout << "can't find input format " << formatName << "\n";
        return false;
    }

    AVFormatContextHandle FormatCtx(avformat_alloc_context());

    if(!FormatCtx.is_valid())
    {
        std::cout << "\n NULL CONTEXT \n ";
        return false;
    }

    if(avformat_open_input (&FormatCtx, capturedUrl.c_str(), pFormat, NULL))
        return false;

    for(int i=0; i<(int)FormatCtx->nb_streams; i++)
    {
        if(FormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO)
        {
            videoStream=i;
            break;
        }
    }
    if(videoStream < 0 )
        return false;

    CodecContextHandle CodecCtx(FormatCtx->streams[videoStream]->codec, avcodec_close);
    AVCodec *pCodec = avcodec_find_decoder(CodecCtx->codec_id);

    if(pCodec == NULL)
        return false;

    if( avcodec_open2(CodecCtx, pCodec, &optionsDict) < 0 )
        return false;

    FrameHandle Frame(avcodec_alloc_frame(), av_free);

    if(!Frame.is_valid())
        return false;

    int frameFinished=0;
    AVPacket packet;

    while(av_read_frame(FormatCtx, &packet)>=0)
    {
        if(packet.stream_index==videoStream)
        {
            avcodec_decode_video2(CodecCtx, Frame, &frameFinished, &packet);

            if(frameFinished)
            {
                std::string uu (capturedUrl);
                size_t pos = capturedUrl.rfind(".mp4");
                uu.replace(pos, 4, "thumbnail.jpg");
                // save the frame to file
                int Bytes = avpicture_get_size(PIX_FMT_YUVJ420P, CodecCtx->width, CodecCtx->height);
                BufferHandle buffer((uint8_t*)av_malloc(Bytes*sizeof(uint8_t)), av_free);
                CodecContextHandle OutContext(avcodec_alloc_context3(NULL), free_context);
                OutContext->bit_rate = CodecCtx->bit_rate;
                OutContext->width = CodecCtx->width;
                OutContext->height = CodecCtx->height;
                OutContext->pix_fmt = PIX_FMT_YUVJ420P;
                OutContext->codec_id = CODEC_ID_MJPEG;
                OutContext->codec_type = AVMEDIA_TYPE_VIDEO;
                OutContext->time_base.num = CodecCtx->time_base.num;
                OutContext->time_base.den = CodecCtx->time_base.den;
                AVCodec *OutCodec = avcodec_find_encoder(OutContext->codec_id);
                avcodec_open2(OutContext, OutCodec, NULL);
                OutContext->mb_lmin = OutContext->lmin = OutContext->qmin * 118;
                OutContext->mb_lmax = OutContext->lmax = OutContext->qmax * 118;
                OutContext->flags = 2;
                OutContext->global_quality = OutContext->qmin * 118;

                Frame->pts = 1;
                Frame->quality = OutContext->global_quality;

                int ActualSize = avcodec_encode_video(OutContext, buffer, Bytes, Frame);
                std::ofstream file("c:\\temp\\output.jpg", std::ios_base::binary | std::ios_base::out);
                file.write((const char*)(uint8_t*)buffer, ActualSize);
            }
            if(CodecCtx->refcounted_frames == 1)
                av_frame_unref(Frame);
        }
        av_free_packet(&packet);
    }

    return true;
}

int main()
{
    av_register_all();
    while(true)
        PullFrame("c:\\temp\\sample_mpeg4.mp4");
    return 0;
}

Problem

I have a problem where only sometimes when I call `avformat_close_input(&pFormatCtx)` and it results in `malloc check failed` and my application crashes. I really need to use `ffmpeg` because I need to grab a thumbnail of a video to show in a list and I cannot find an alternative library. Can anybody see something in my code where I am doing something wrong when using this library which may cause this malloc check failed problem? ``` bool MuteCamera::PullFrame( ) { pMJPEGCodec = avcodec_find_encoder(CODEC_ID_MJPEG ); bool bRet = false; int videoStream = -1; AVFrame *pFrame=NULL; AVFrame *pFrameRGB=NULL; AVPacket packet; int frameFinished=0; //AVDictionary *optionsDict = NULL; AVInputFormat *pFormat = NULL; const char formatName[] = "mp4"; if (!(pFormat = av_find_input_format(formatName))) { printf("can't find input format %s\n", formatName); return -1; } AVFormatContext *pFormatCtx = NULL; pFormatCtx=avformat_alloc_context(); if(pFormatCtx == NULL) { printf("\n NULL CONTEXT \n "); return -1; } if(avformat_open_input (&pFormatCtx, capturedUrl.data(), pFormat, NULL) == 0 ) { for(int i=0; i<(int)pFormatCtx->nb_streams; i++) { if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO) { videoStream=i; break; } } if(videoStream >= 0 ) { AVCodecContext *pCodecCtx = pFormatCtx->streams[videoStream]->codec; AVCodec *pCodec = avcodec_find_decoder(pCodecCtx->codec_id); if(pCodec != NULL) { if( avcodec_open2(pCodecCtx, pCodec, NULL) >= 0 ) { pFrame=avcodec_alloc_frame(); if(pFrame != NULL) { frameFinished = 0; while(av_read_frame(pFormatCtx, &packet)>=0) { if(packet.stream_index==videoStream) { avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet); if(frameFinished) { printf("\n FRAMEFINISHED \n "); QString *uu = new QString(capturedUrl.data());// uu->replace(".mp4", "thumbnail.jpg"); WriteJPEG(pCodecCtx, pFrame, uu->toLatin1().data(), PIX_FMT_YUVJ420P); if(viewingVideos && viewingFromDifferent) { QVariantMap map = QVariantMap(); map["title"] = actualFilename; map["path"] = actualFilename.replace(".mp4", "thumbnail.jpg");// QString("asset:///white_photo.png"); m_listDataModel << map; } delete uu; av_free_packet(&packet); break; } else { printf("\n FRAMENOTFINISHED \n "); } } av_free_packet(&packet); } av_free(pFrameRGB); av_free(pFrame); avcodec_close(pCodecCtx); //av_free(pCodecCtx); cout << "\n before free formatctx \n"; cout.flush(); if(pFormatCtx) avformat_close_input(&pFormatCtx); cout << "\n after free formatctx \n"; cout.flush(); } else bRet = false; } else bRet = false; } else bRet = false; } else bRet = false; } return bRet; } bool WriteJPEG (AVCodecContext *pCodecCtx, AVFrame *pFrame, char cFileName[], PixelFormat pix) { int complete = 0; bool bRet = false; int out_buf_size; uint8_t *out_buf; AVCodecContext *pMJPEGCtx = avcodec_alloc_context3(pMJPEGCodec); if( pMJPEGCtx ) { pMJPEGCtx->bit_rate = pCodecCtx->bit_rate; pMJPEGCtx->width = pCodecCtx->width; pMJPEGCtx->height = pCodecCtx->height; pMJPEGCtx->pix_fmt = pix; pMJPEGCtx->codec_id = CODEC_ID_MJPEG; pMJPEGCtx->codec_type = AVMEDIA_TYPE_VIDEO; pMJPEGCtx->time_base.num = pCodecCtx->time_base.num; pMJPEGCtx->time_base.den = pCodecCtx->time_base.den; pMJPEGCtx->time_base= (AVRational){1,29.7}; if( pMJPEGCodec && (avcodec_open2( pMJPEGCtx, pMJPEGCodec, NULL) >= 0) ) { AVFrame *oframe; oframe = avcodec_alloc_frame(); if(oframe == NULL) { printf("\n (oframe == NULL"); fflush(stdout); } /* calculate the bytes needed for the output image and create buffer for the output image */ out_buf_size = avpicture_get_size(pMJPEGCtx->pix_fmt, pMJPEGCtx->width, pMJPEGCtx->height); out_buf = (uint8_t *)av_malloc(out_buf_size * sizeof(uint8_t)); if (out_buf == NULL) { fprintf(stderr, "cannot allocate output data buffer!\n"); //ret = -ENOMEM; } avpicture_alloc((AVPicture *)oframe, pMJPEGCtx->pix_fmt, pMJPEGCtx->width, pMJPEGCtx->height); struct SwsContext *sws; sws = sws_getContext(pMJPEGCtx->width, pMJPEGCtx->height, pCodecCtx->pix_fmt, pMJPEGCtx->width, pMJPEGCtx->height, pMJPEGCtx->pix_fmt, SWS_BILINEAR, NULL, NULL, NULL); sws_scale(sws, (const uint8_t **)pFrame->data, pFrame->linesize, 0, pMJPEGCtx->height, &oframe->data[0], &oframe->linesize[0]); sws_freeContext(sws); AVPacket pp2; av_init_packet(&pp2); pp2.data = NULL; pp2.size = 0; avcodec_encode_video2(pMJPEGCtx, &pp2, oframe, &complete); if(complete) { printf("\n packet recieved"); fflush(stdout); } else { printf("\n packet NOT recieved"); fflush(stdout); } if( SaveFrameJpeg(pp2.size, pp2.data, cFileName ) ) bRet = true; av_free(oframe); avcodec_close(pMJPEGCtx); av_free_packet(&pp2); av_free(out_buf); av_free(pMJPEGCtx); } else { printf("\n problem!!"); fflush(stdout); } return bRet; } } bool SaveFrameJpeg(int nszBuffer, uint8_t *buffer, char cOutFileName[]) { bool bRet = false; FILE *pFile; if( nszBuffer > 0 ) { if(0 == 0 ) { printf("\n start SaveFrameJpeg=%d",nszBuffer ); fflush(stdout); pFile= fopen(cOutFileName, "wb"); fwrite(buffer, sizeof(uint8_t), nszBuffer, pFile); bRet = true; fclose(pFile); printf("\n end SaveFrameJpeg=%d",nszBuffer ); fflush(stdout); } } return bRet; } bool newPullFrame(const std::string& capturedUrl) { AVCodec* pMJPEGCodec = avcodec_find_encoder(CODEC_ID_MJPEG ); int videoStream = -1; AVDictionary *optionsDict = NULL; AVInputFormat *pFormat = NULL; const char formatName[] = "mp4"; if (!(pFormat = av_find_input_format(formatName))) { std::cout << "can't find input format " << formatName << "\n"; return false; } AVFormatContextHandle FormatCtx(avformat_alloc_context()); if(!FormatCtx.is_valid()) { std::cout << "\n NULL CONTEXT \n "; return false; } if(avformat_open_input (&FormatCtx, capturedUrl.c_str(), pFormat, NULL)) return false; for(int i=0; i<(int)FormatCtx->nb_streams; i++) { if(FormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO) { videoStream=i; break; } } if(videoStream < 0 ) return false; CodecContextHandle CodecCtx(FormatCtx->streams[videoStream]->codec, avcodec_close); AVCodec *pCodec = avcodec_find_decoder(CodecCtx->codec_id); if(pCodec == NULL) return false; if( avcodec_open2(CodecCtx, pCodec, &optionsDict) < 0 ) return false; FrameHandle Frame(avcodec_alloc_frame(), av_free); if(!Frame.is_valid()) return false; int frameFinished=0; AVPacket packet; while(av_read_frame(FormatCtx, &packet)>=0) { if(packet.stream_index==videoStream) { avcodec_decode_video2(CodecCtx, Frame, &frameFinished, &packet); if(frameFinished) { std::string uu (capturedUrl); size_t pos = capturedUrl.rfind(".mp4"); uu.replace(pos, 4, "thumbnail.jpg"); // save the frame to file int Bytes = avpicture_get_size(PIX_FMT_YUVJ420P, CodecCtx->width, CodecCtx->height); BufferHandle buffer((uint8_t*)av_malloc(Bytes*sizeof(uint8_t)), av_free); CodecContextHandle OutContext(avcodec_alloc_context3(NULL), free_context); OutContext->bit_rate = CodecCtx->bit_rate; OutContext->width = CodecCtx->width; OutContext->height = CodecCtx->height; OutContext->pix_fmt = PIX_FMT_YUVJ420P; OutContext->codec_id = CODEC_ID_MJPEG; OutContext->codec_type = AVMEDIA_TYPE_VIDEO; OutContext->time_base.num = CodecCtx->time_base.num; OutContext->time_base.den = CodecCtx->time_base.den; OutContext->time_base= (AVRational){1,29.7}; AVCodec *OutCodec = avcodec_find_encoder(OutContext->codec_id); avcodec_open2(OutContext, OutCodec, NULL); OutContext->mb_lmin = OutContext->lmin = OutContext->qmin * 118; OutContext->mb_lmax = OutContext->lmax = OutContext->qmax * 118; OutContext->flags = 2; OutContext->global_quality = OutContext->qmin * 118; Frame->pts = 1; Frame->quality = OutContext->global_quality; int ActualSize = avcodec_encode_video(OutContext, buffer, Bytes, Frame); std::ofstream file(uu.data(), std::ios_base::binary | std::ios_base::out); file.write((const char*)(uint8_t*)buffer, ActualSize); file.close(); av_free_packet(&packet); av_free(Frame); break; } else { std::cout << " new pullframe frameNOTfinished\n"; cout.flush(); } //if(CodecCtx->refcounted_frames == 1) av_free(Frame); } av_free_packet(&packet); } return true; } ```

Original source