FFmpeg中AVPacket、AVFrame结构的基本使用

简介: FFmpeg中AVPacket和AVFrame结构的内存分配、释放和引用计数处理,以及如何避免内存泄漏。

AVPacket结构以及一些API的使用

#include <iostream>

extern "C"
{
#include <libavcodec\packet.h>
}

// 1. 测试仅alloc不free会不会导致内存泄漏
void test01()
{
    AVPacket* pkt = NULL;
    while (true)
    {
        pkt = av_packet_alloc();    // 内存会一直增长(由此可见内部不会自己释放)
        _sleep(1);
    }
}

// 2. 测试alloc与free配合使用
void test02()
{
    AVPacket* pkt = NULL;
    while (true)
    {
        pkt = av_packet_alloc();        // 分配后释放,不会导致内存泄漏
        _sleep(1);
        av_packet_free(&pkt);
    }
}

// 3. 使用new为pkt里的buff(AVBufferRef)分配内存,查看引用计数
void test03()
{
    AVPacket* pkt = NULL;
    pkt = av_packet_alloc();    // 申请packet内存(bufferRef 0x00)
    av_new_packet(pkt, 1024);    // 申请AVBufferRef内存 (引用计数初始化为1)
    std::cout << "buffRef Cnt: " << av_buffer_get_ref_count(pkt->buf) << std::endl;    // 1
    // av_packet_unref(pkt);    // 这里不需要调(av_packet_free调用一次av_packet_unref)
    av_packet_free(&pkt);
}

// 4. 误使用init函数导致buffRef指向的内存无法被释放
void test04()
{
    AVPacket* pkt = NULL;
    while (true)
    {
        pkt = av_packet_alloc();
        av_new_packet(pkt, 1024);
        av_init_packet(pkt);        // 指向AVBuffRef的buf指针被置空,导致内存咔咔漏
        av_packet_free(&pkt);        // AVBuffRef的buf内存无法释放
        _sleep(1);
    }
    // 注意: 还有一个av_free_packet函数
    // av_packet_free 清空buf,然后将pkt置空
    // av_free_packet 清空buf,并不会将pkt置空
}

// 5. av_packet_move_ref的使用
void test05()
{
    AVPacket* pkt1 = NULL;
    AVPacket* pkt2 = NULL;

    pkt1 = av_packet_alloc();
    av_new_packet(pkt1, 1024);
    pkt2 = av_packet_alloc();            // 必须先给pkt2分配内存
    av_packet_move_ref(pkt2, pkt1);        // 将pkt1的AVBuffRef以及其它参数移交给pkt2,将pkt1参数初始化init
    av_packet_free(&pkt1);                // 这里均要将pkt的内存释放掉
    av_packet_free(&pkt2);                // 因为此时引用计数为1,内部一次uref就能释放buf内存
}

// 6. av_packet_clone使用
void test06()
{
    AVPacket* pkt1 = NULL;
    AVPacket* pkt2 = NULL;

    while (true)
    {
        pkt1 = av_packet_alloc();
        av_new_packet(pkt1, 1024);
        pkt2 = av_packet_clone(pkt1);    // av_packet_alloc + av_packet_ref

        av_packet_free(&pkt1);            // 两次free会将引用计数为2的buf清除(不会造成内存泄漏)
        av_packet_free(&pkt2);
    }
}

// 7. av_packet_clone 后使用  av_init_packet 会导致buf内存无法释放
void test07()
{
    AVPacket* pkt1 = NULL;
    AVPacket* pkt2 = NULL;

    while (true)
    {
        pkt1 = av_packet_alloc();
        av_new_packet(pkt1, 1024);
        pkt2 = av_packet_clone(pkt1);    

        av_init_packet(pkt1);            // 导致av_packet_free(pkt1) 引用计数不减(导致无法释放) 内存咔咔漏

        av_packet_free(&pkt1);            
        av_packet_free(&pkt2);
    }
}

// 8. AVPacket整个结构体赋值, 和av_packet_move_ref类似
void test08()
{
    AVPacket* pkt1 = NULL;
    AVPacket* pkt2 = NULL;

    while (true)
    {
        pkt1 = av_packet_alloc();
        av_new_packet(pkt1, 1024);

        pkt2 = av_packet_alloc();
        *pkt2 = *pkt1;

        av_init_packet(pkt1);
        av_packet_free(&pkt1);    // 需要将pkt1的内存释放掉(否则会导致内存泄漏)
        av_packet_free(&pkt2);
    }
}

// 9. 多次使用 av_packet_ref 导致内存无法释放的问题
void test09()
{
    AVPacket* pkt1 = NULL;
    AVPacket* pkt2 = NULL;

    while (true)
    {
        pkt1 = av_packet_alloc();
        av_new_packet(pkt1, 1024);    // 引用计数加一

        pkt2 = av_packet_alloc();

        av_packet_ref(pkt2, pkt1);
        av_packet_ref(pkt2, pkt1);    // 此时引用计数为3

        av_packet_free(&pkt1);        // 但是仅仅引用计数减两次(导致内存泄漏)
        av_packet_free(&pkt2);
    }
}

// 10. 先uref一次呢???
void test10()
{
    AVPacket* pkt1 = NULL;
    AVPacket* pkt2 = NULL;

    while (true)
    {
        pkt1 = av_packet_alloc();
        av_new_packet(pkt1, 1024);

        pkt2 = av_packet_alloc();

        av_packet_ref(pkt2, pkt1);
        av_packet_ref(pkt2, pkt1);    // 此时引用计数为3

        av_packet_unref(pkt1);        // 内存还是会泄露,目前为两个pkt指向同一个AVBuffRef
        av_packet_free(&pkt1);        // 两次unref(pkt1) 会有一次无效
        av_packet_free(&pkt2);        // 从而导致内存咔咔漏
    }
}

// 11. 正确使用
void test11()
{
    AVPacket* pkt1 = NULL;
    AVPacket* pkt2 = NULL;

    while (true)
    {
        pkt1 = av_packet_alloc();
        av_new_packet(pkt1, 1024);

        pkt2 = av_packet_alloc();

        av_packet_ref(pkt2, pkt1);    // 增加引用
        av_packet_unref(pkt1);        // 减少引用(此时AVBuffRef被pkt2持有)
        if (pkt1->buf)
        {
            std::cout << "pkt1 has buf" << std::endl;
        }
        if (pkt2->buf)
        {
            std::cout << "pkt2 has buf" << std::endl;    // 被打印
        }
        av_packet_ref(pkt2, pkt1);    // 此时引用计数为2


        av_packet_free(&pkt1);        // 两次释放将buf释放掉
        av_packet_free(&pkt2);        
    }
}

AVFrame结构以及一些API的使用

#include <iostream>

extern "C"
{
#include <libavutil\frame.h>
#include <libavcodec\avcodec.h>
}

// 1. allo与free配对使用
void frame_test01()
{
    AVFrame* frame = NULL;    

    while (true)
    {
        frame = av_frame_alloc();    
        av_frame_free(&frame);    // 不配对使用会导致内存泄漏
    }
}

// 2. 根据格式分配内存
void frame_test02()
{
    AVFrame* frame = NULL;
    while (true)
    {
        frame = av_frame_alloc();
        frame->nb_samples = 1024;
        frame->format = AV_SAMPLE_FMT_S16P;                    // AV_SAMPLE_FMT_S16P AV_SAMPLE_FMT_S16
        frame->channel_layout = AV_CH_LAYOUT_STEREO;        // AV_CH_LAYOUT_MONO AV_CH_LAYOUT_STEREO(立体音)
        av_frame_get_buffer(frame, 0);                        // frame中的buf会根据格式自动分配内存
        // buf有多个,因为数据排列的格式不一样(yuv pcm) 
        // 当为音频帧 平面模式且双声道时(左右声道数据会分开存储)为交错模式时(数据存储在一起)
        if (frame->buf && frame->buf[0])
        {
            std::cout << "buf[0] size: " << frame->buf[0]->size << std::endl;    
        }
        if (frame->buf && frame->buf[1])
        {
            std::cout << "buf[1] size: " << frame->buf[1]->size << std::endl;
        }
        // AV_SAMPLE_FMT_S16 AV_CH_LAYOUT_STEREO  交错立体音(一个 buf[0] size 1024 * 2 * 2 = 4096)
        // AV_SAMPLE_FMT_S16P AV_CH_LAYOUT_STEREO 平面立体音(两个 buf[0] size:2048 buf[1]size:2048) 平面且双声道分开存储

        // AV_SAMPLE_FMT_S16 AV_CH_LAYOUT_MONO  交错单声道(一个 buf[0] size 1024 * 2 = 2048)
        // AV_SAMPLE_FMT_S16P AV_CH_LAYOUT_MONO 平面单声道(一个 buf[0] size:2048)


        // 2. av_frame_make_writable (当frame本声为空时不能av_frame_make_writable)
        if (frame)
        {
            av_frame_make_writable(frame);    // 分配新缓冲,赋值frame数据,防止和解码器造成冲突
        }

        // av_frame_unref(frame);            // av_frame_free内部有引用计数减一操作
        av_frame_free(&frame);    
    }
}

int main_frame()
{
    //frame_test01();
    frame_test02();

    getchar();
    return 0;
}
相关文章
|
28天前
|
弹性计算 人工智能 架构师
阿里云携手Altair共拓云上工业仿真新机遇
2024年9月12日,「2024 Altair 技术大会杭州站」成功召开,阿里云弹性计算产品运营与生态负责人何川,与Altair中国技术总监赵阳在会上联合发布了最新的“云上CAE一体机”。
阿里云携手Altair共拓云上工业仿真新机遇
|
4天前
|
人工智能 Rust Java
10月更文挑战赛火热启动,坚持热爱坚持创作!
开发者社区10月更文挑战,寻找热爱技术内容创作的你,欢迎来创作!
442 17
|
7天前
|
JSON 自然语言处理 数据管理
阿里云百炼产品月刊【2024年9月】
阿里云百炼产品月刊【2024年9月】,涵盖本月产品和功能发布、活动,应用实践等内容,帮助您快速了解阿里云百炼产品的最新动态。
阿里云百炼产品月刊【2024年9月】
|
20天前
|
存储 关系型数据库 分布式数据库
GraphRAG:基于PolarDB+通义千问+LangChain的知识图谱+大模型最佳实践
本文介绍了如何使用PolarDB、通义千问和LangChain搭建GraphRAG系统,结合知识图谱和向量检索提升问答质量。通过实例展示了单独使用向量检索和图检索的局限性,并通过图+向量联合搜索增强了问答准确性。PolarDB支持AGE图引擎和pgvector插件,实现图数据和向量数据的统一存储与检索,提升了RAG系统的性能和效果。
|
7天前
|
Linux 虚拟化 开发者
一键将CentOs的yum源更换为国内阿里yum源
一键将CentOs的yum源更换为国内阿里yum源
381 2
|
22天前
|
人工智能 IDE 程序员
期盼已久!通义灵码 AI 程序员开启邀测,全流程开发仅用几分钟
在云栖大会上,阿里云云原生应用平台负责人丁宇宣布,「通义灵码」完成全面升级,并正式发布 AI 程序员。
|
24天前
|
机器学习/深度学习 算法 大数据
【BetterBench博士】2024 “华为杯”第二十一届中国研究生数学建模竞赛 选题分析
2024“华为杯”数学建模竞赛,对ABCDEF每个题进行详细的分析,涵盖风电场功率优化、WLAN网络吞吐量、磁性元件损耗建模、地理环境问题、高速公路应急车道启用和X射线脉冲星建模等多领域问题,解析了问题类型、专业和技能的需要。
2600 22
【BetterBench博士】2024 “华为杯”第二十一届中国研究生数学建模竞赛 选题分析
|
6天前
|
存储 人工智能 搜索推荐
数据治理,是时候打破刻板印象了
瓴羊智能数据建设与治理产品Datapin全面升级,可演进扩展的数据架构体系为企业数据治理预留发展空间,推出敏捷版用以解决企业数据量不大但需构建数据的场景问题,基于大模型打造的DataAgent更是为企业用好数据资产提供了便利。
289 2
|
4天前
|
编译器 C#
C#多态概述:通过继承实现的不同对象调用相同的方法,表现出不同的行为
C#多态概述:通过继承实现的不同对象调用相同的方法,表现出不同的行为
106 65
|
24天前
|
机器学习/深度学习 算法 数据可视化
【BetterBench博士】2024年中国研究生数学建模竞赛 C题:数据驱动下磁性元件的磁芯损耗建模 问题分析、数学模型、python 代码
2024年中国研究生数学建模竞赛C题聚焦磁性元件磁芯损耗建模。题目背景介绍了电能变换技术的发展与应用,强调磁性元件在功率变换器中的重要性。磁芯损耗受多种因素影响,现有模型难以精确预测。题目要求通过数据分析建立高精度磁芯损耗模型。具体任务包括励磁波形分类、修正斯坦麦茨方程、分析影响因素、构建预测模型及优化设计条件。涉及数据预处理、特征提取、机器学习及优化算法等技术。适合电气、材料、计算机等多个专业学生参与。
1582 17
【BetterBench博士】2024年中国研究生数学建模竞赛 C题:数据驱动下磁性元件的磁芯损耗建模 问题分析、数学模型、python 代码