3

我有一个问题,只有在我打电话时avformat_close_input(&pFormatCtx)才会导致malloc check failed我的应用程序崩溃。

我真的需要使用ffmpeg,因为我需要获取视频的缩略图以显示在列表中,而我找不到替代库。

任何人都可以在我的代码中看到我在使用这个库时做错了什么,这可能会导致这个 malloc 检查失败的问题吗?

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

1 回答 1

6

在我看来,您需要将调用移至av_free_packet(&packet)while 循环内。所以你目前有:

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

相反,你应该有

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

有关详细信息,请参见此处

考虑到您是用 C++ 编写的,您可以考虑围绕这些资源创建一些简单的RAII包装器,以使您的资源管理更加容易。

编辑

根据您的反馈,我建议的这个更改似乎没有做到。所以我重新审视了代码。我实现了我自己的 Jpeg 保存,因为我看不到你的 - 尽管出于测试目的,它只是保持覆盖同一个文件。

为了简化代码并掌握资源管理,我为 ffmpeg 资源实现了一些“智能指针”。这些会在范围退出时自动清理。运行此代码时,我看不到资源泄漏,并且它正确地将每个帧生成为 jpeg 文件。

看看你是否从中得到任何价值:

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;
}
于 2013-04-27T14:24:14.737 回答