Как освободить ресурсы от ffmpeg - PullRequest
0 голосов
/ 10 февраля 2020

Я создал класс, который читает файл 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 мой вопрос заключается в том, правильно ли я освободил память.

1 Ответ

1 голос
/ 11 февраля 2020

В вашем коде две очевидные проблемы.

  1. Вы должны использовать av_frame_unref() вместо av_freep() для AVFrames.
  2. avpicture_fill устарела долго a go. Если ваша установка ffmpeg старая. Обнови это. В противном случае используйте av_image_fill_arrays() вместо.

С современной документацией API можно ознакомиться здесь: http://www.ffmpeg.org/doxygen/trunk/index.html

Надеюсь, что это поможет.

...