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;
}
相关文章
|
2月前
FFmpeg中结构释放小函数
本文介绍了FFmpeg中用于释放不同结构体内存的泛化变参模板函数CleanUp,以及如何特化该模板以释放AVFormatContext、AVCodecContext、AVPacket、AVFrame和uint8_t*类型的内存,并提供了一个测试文件来演示这些函数的使用。
27 3
|
7月前
|
编解码 算法 vr&ar
深度剖析FFmpeg视频解码后的帧处理到Qt显示 从AVFrame到QImage的转换(二)
深度剖析FFmpeg视频解码后的帧处理到Qt显示 从AVFrame到QImage的转换
249 1
|
7月前
|
存储 编解码 算法
深度剖析FFmpeg视频解码后的帧处理到Qt显示 从AVFrame到QImage的转换(一)
深度剖析FFmpeg视频解码后的帧处理到Qt显示 从AVFrame到QImage的转换
510 1
|
7月前
|
安全 数据处理 数据格式
深入浅出:FFmpeg 音频解码与处理AVFrame全解析(三)
深入浅出:FFmpeg 音频解码与处理AVFrame全解析
324 0
|
7月前
|
存储 编解码 索引
了解FFmpeg音频通道布局结构:AVChannelLayout结构体解析
了解FFmpeg音频通道布局结构:AVChannelLayout结构体解析
251 1
|
7月前
|
存储 算法 编译器
【ffmpeg 到Qt的图片格式转换】精彩的像素:深入解析 AVFrame 到 QImage 的转换
【ffmpeg 到Qt的图片格式转换】精彩的像素:深入解析 AVFrame 到 QImage 的转换
271 0
|
7月前
|
存储 编解码 数据处理
深入浅出:FFmpeg 音频解码与处理AVFrame全解析(二)
深入浅出:FFmpeg 音频解码与处理AVFrame全解析
564 0
|
7月前
|
存储 编解码 算法
深入浅出:FFmpeg 音频解码与处理AVFrame全解析(一)
深入浅出:FFmpeg 音频解码与处理AVFrame全解析
1402 0
|
容器
FFmpeg读取文件内容AVPacket
FFmpeg读取文件内容AVPacket
232 0
|
存储 编解码 算法
【Android FFMPEG 开发】FFMPEG AVFrame 图像格式转换 YUV -> RGBA ( 获取 SwsContext | 初始化图像数据存储内存 | 图像格式转换 )
【Android FFMPEG 开发】FFMPEG AVFrame 图像格式转换 YUV -> RGBA ( 获取 SwsContext | 初始化图像数据存储内存 | 图像格式转换 )
437 0