首页 > 解决方案 > 如何从ffmpeg释放资源

问题描述

我已经建立了一个读取 avi 文件并显示它的类。

这是类的定义。

typedef struct {

    AVFormatContext *fmt_ctx;
    int stream_idx;
    AVStream *video_stream;
    AVCodecContext *codec_ctx;
    AVCodec *decoder;
    AVPacket *packet;
    AVFrame *av_frame;
    AVFrame *gl_frame;
    struct SwsContext *conv_ctx;
    unsigned int  frame_tex;    
}AppData;


class ClipPlayer{

private:    
    AppData data;   
    std::vector< AVFrame* > cache;
public:

    ClipPlayer();
    ClipPlayer(const  ClipPlayer& player);
    ClipPlayer& operator=(const  ClipPlayer& player);
         ~ClipPlayer();
    void initializeAppData();
    void clearAppData();
    bool readFrame();
    bool initReadFrame();
    void playCache();   
    void init();
    void draw();
    void reset();
    }

在 init 函数中读取 AVI 文件并将帧保存在内存中。

void init()
{

initializeAppData();

    // open video
    if (avformat_open_input(&data.fmt_ctx, stdstrPathOfVideo.c_str(), NULL, NULL) < 0) {
        clearAppData();
        return;
    }    
    // find stream info
    if (avformat_find_stream_info(data.fmt_ctx, NULL) < 0) {
        clearAppData();
        return;
    } 
    // find the video stream
    for (unsigned int i = 0; i < data.fmt_ctx->nb_streams; ++i)
    {
        if (data.fmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            data.stream_idx = i;
            break;
        }
    }

    if (data.stream_idx == -1)
    {
        clearAppData();
        return;
    }

    data.video_stream = data.fmt_ctx->streams[data.stream_idx];
    data.codec_ctx = data.video_stream->codec;

    // find the decoder
    data.decoder = avcodec_find_decoder(data.codec_ctx->codec_id);
    if (data.decoder == NULL)
    {
        clearAppData();
        return;
    }

    // open the decoder
    if (avcodec_open2(data.codec_ctx, data.decoder, NULL) < 0)
    {
        clearAppData();
        return;
    }

    // allocate the video frames
    data.av_frame = av_frame_alloc();
    data.gl_frame = av_frame_alloc();
    int size = avpicture_get_size(AV_PIX_FMT_RGBA, data.codec_ctx->width,
        data.codec_ctx->height);
    uint8_t *internal_buffer = (uint8_t *)av_malloc(size * sizeof(uint8_t));
    avpicture_fill((AVPicture *)data.gl_frame, internal_buffer, AV_PIX_FMT_RGBA,
        data.codec_ctx->width, data.codec_ctx->height);
    data.packet = (AVPacket *)av_malloc(sizeof(AVPacket));
}

///////////////////////////////////////// ///////////

bool ClipPlayer::initReadFrame()
{
    do {
        glBindTexture(GL_TEXTURE_2D, data.frame_tex);
        int error = av_read_frame(data.fmt_ctx, data.packet);       
        if (error)
        {           
            av_free_packet(data.packet);
            return false;
        }

        if (data.packet->stream_index == data.stream_idx)
        {
            int frame_finished = 0;
            if (avcodec_decode_video2(data.codec_ctx, data.av_frame, &frame_finished,
                data.packet) < 0) {
                av_free_packet(data.packet);
                return false;
            }
            if (frame_finished)
            {               
                if (!data.conv_ctx)
                {
                    data.conv_ctx = sws_getContext(data.codec_ctx->width,
                        data.codec_ctx->height, data.codec_ctx->pix_fmt,
                        data.codec_ctx->width, data.codec_ctx->height, AV_PIX_FMT_RGBA,
                        SWS_BICUBIC, NULL, NULL, NULL);
                }
                sws_scale(data.conv_ctx, data.av_frame->data, data.av_frame->linesize, 0,
                    data.codec_ctx->height, data.gl_frame->data, data.gl_frame->linesize);

                glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, data.codec_ctx->width,
                    data.codec_ctx->height, GL_RGBA, GL_UNSIGNED_BYTE,
                    data.gl_frame->data[0]);    

                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

                AVFrame *cachedValue = av_frame_alloc();
                cachedValue->format = data.av_frame->format;
                cachedValue->width = data.av_frame->width;
                cachedValue->height = data.av_frame->height;
                cachedValue->channels = data.av_frame->channels;
                cachedValue->channel_layout = data.av_frame->channel_layout;
                cachedValue->nb_samples = data.av_frame->nb_samples;
                av_frame_get_buffer(cachedValue, 32);
                av_frame_copy(cachedValue, data.av_frame);
                av_frame_copy_props(cachedValue, data.av_frame);
                cache.push_back((cachedValue));
            }
        }       
    } while (data.packet->stream_index != data.stream_idx);

///////////////////////////////////////// /////////////////

在播放缓存功能中显示帧

void ClipPlayer::playCache()
{
       glActiveTexture(GL_TEXTURE0);
       sws_scale(data.conv_ctx, cache[loop]->data, cache[loop]->linesize, 0,
       data.codec_ctx->height, data.gl_frame->data, data.gl_frame->linesize);
       glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, data.codec_ctx->width,
        data.codec_ctx->height, GL_RGBA, GL_UNSIGNED_BYTE,data.gl_frame->data[0]);
       glBindTexture(GL_TEXTURE_2D, data.frame_tex);    
}

在析构函数中,我尝试释放内存

~ClipPlayer()
{
     for (auto &frame : cache)
        {
         av_freep(frame);
        }
}

我对使用 FFmpeg 不是很熟练,我的问题是我是否正确释放了内存。

标签: ffmpegmemory-leaks

解决方案


你的代码有两个明显的问题。

  1. 您应该使用av_frame_unref()而不是av_freep()用于 AVFrames。
  2. avpicture_fill很久以前就被弃用了。如果您的 ffmpeg 安装旧。更新它。否则av_image_fill_arrays()改用。

您可以在此处参考最新的 API 文档:http ://www.ffmpeg.org/doxygen/trunk/index.html

希望有帮助。


推荐阅读