ffmpeg学习日记22-内存读取avio_alloc_context函数的内存释放问题

在开发内存读写数据过程中,遇到了内存泄露的问题,泄漏点围绕着avio_alloc_context函数,在内存中读写数据参考雷神文章ffmpeg 从内存中读取数据(或将数据输出到内存),在雷神的文章中,只讲述了如何实现内存读写的功能,没有讲到内存释放,或许这根本就不是问题。

    // Input
    if (!(ifmt_ctx_v = avformat_alloc_context())) {
        std::cout << "error!!! ifmt_ctx_v = avformat_alloc_context()" << std::endl;
        exit(0);
    }
    if (!(ifmt_ctx_a = avformat_alloc_context())) {
        std::cout << "error!!! ifmt_ctx_a = avformat_alloc_context()" << std::endl;
        exit(0);
    }

    // iobuff_v数据需要释放
    unsigned char *iobuff_v = (unsigned char *)av_malloc(INBUF_SIZE); // 32768
    if (iobuff_v == NULL) {
        std::cout << "error!!! malloc " << std::endl;
        exit(0);
    }
    AVIOContext *avio = avio_alloc_context(iobuff_v, INBUF_SIZE, 0, NULL, fill_iobuff_v, NULL, NULL);
    unsigned char *iobuff_a = (unsigned char *)av_malloc(INBUF_SIZE);
    if (iobuff_a == NULL) {
        std::cout << "error!!! malloc " << std::endl;
        exit(0);
    }
    AVIOContext *avio_a = avio_alloc_context(iobuff_a, INBUF_SIZE, 0, NULL, fill_iobuff_a, NULL, NULL);

    ifmt_ctx_v->pb = avio;
    ifmt_ctx_v->flags = AVFMT_FLAG_CUSTOM_IO;
    ifmt_ctx_a->pb = avio_a;
    ifmt_ctx_a->flags = AVFMT_FLAG_CUSTOM_IO;

    if ((ret = avformat_open_input(&ifmt_ctx_v, NULL, NULL, NULL)) < 0) {
        fprintf(stderr, "Could not open input file.%d", ret);
        goto end;
    }

    std::cout << "before avformat_open_input : " << filename_output << std::endl;
    if ((ret = avformat_find_stream_info(ifmt_ctx_v, NULL)) < 0) {
        printf("Failed to retrieve input stream information");
        goto end;
    }
    std::cout << "after avformat_open_input " << std::endl;

    if ((ret = avformat_open_input(&ifmt_ctx_a, NULL, NULL, NULL)) < 0) {
        printf("Could not open input file.");
        goto end;
    }
    if ((ret = avformat_find_stream_info(ifmt_ctx_a, NULL)) < 0) {
        printf("Failed to retrieve input stream information");
        goto end;
    }

上面的代码是从内存中读取数据,申请了iobuff_v空间,iobuff_v和AVIOContext *avio 进行关联,

 ifmt_ctx_v->pb = avio;

avio和ifmt_ctx_v->pb进行关联,这样一个关联关系,在进行内存释放的时候,使用如下函数

avio_context_free(&avio_a);
av_free(ifmt_ctx_v->pb);
avio_close(ifmt_ctx_v->pb);
av_free(iobuff_v);
avformat_close_input(&ifmt_ctx_v);
free(iobuff_v);

都不能释放iobuff_v的内存,强行释放就是各种内存错误,在参考了ffmpeg的自带例程之后,发现这个iobuff_v的内存需要这样释放

av_freep(&avio->buffer);

整个内存释放逻辑是:

end:
    if (avio) {
        av_freep(&avio->buffer);
    }
    avio_context_free(&avio);
    if (avio_a) {
        av_freep(&avio_a->buffer);
        avio_context_free(&avio_a);
    }

    if (ifmt_ctx_v) {
        //        av_free(ifmt_ctx_v->pb);
        //        avio_close(ifmt_ctx_v->pb);
        //        av_free(iobuff_v);
        avformat_close_input(&ifmt_ctx_v);

        //        avformat_free_context(ifmt_ctx_v);
    }
    if (ifmt_ctx_a) {
        //        av_free(ifmt_ctx_a->pb);
        //        avio_close(ifmt_ctx_a->pb);
        //        av_free(iobuff_a);
        avformat_close_input(&ifmt_ctx_a);

        //        avformat_free_context(ifmt_ctx_a);
        /* close output */
    }

    if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE)) avio_close(ofmt_ctx->pb);
    avformat_free_context(ofmt_ctx);
    if (ret < 0 && ret != AVERROR_EOF) {
        printf("Error occurred.\n");
        return -1;
    }

    return 0;

奇怪的地方来了,下另一处,我使用了写内存的方式,写内存的释放内容如下:

cleanup:
    if (fifo) av_audio_fifo_free(fifo);
    swr_free(&resample_ctx);
    if (out_codec_ctx) avcodec_free_context(&out_codec_ctx);
    if (out_format_ctx) {
        av_free(out_format_ctx->pb);
        av_free(outbuff);
        avformat_free_context(out_format_ctx);
    }
    if (in_codec_ctx) avcodec_free_context(&in_codec_ctx);
    if (in_format_ctx) avformat_close_input(&in_format_ctx);
    return ret;

在此处就可以使用av_free(out_format_ctx->pb);释放pb,上面却不行,真是奇怪的事。这一点还要继续研究验证。

note

此处有一个点需要注意一下,在ffmpeg中,不同函数和类申请内存,需要使用对应的函数和类的释放函数去释放,否则就会出现释放内存不彻底,内存泄露,或者直接报错等等。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐