当前位置:   article > 正文

ffmpeg 内存模型

ffmpeg 内存模型

最近在学习ffmpeg,阅读了一些packet和frame关于内存操作的api。在此长话短说,只说核心点。

ffmpeg模型

AVFrame  表示编码前的原始数据帧,AVPacket 表示编码后的压缩数据包

问题:

(1)从av_read_frame读取到一个AVPacket后怎么放入队列?

(2)从avcodec_recevice_frame读取到一个AVFrame后又怎么放入队列?

一句话就是:通过std::move转移,既然是move那就有内存模型。从现有的Packet拷贝一个新Packet的时候,有两种情况:

• ①两个Packet的buf引用的是同一数据缓存空间,这时 候要注意数据缓存空间的释放问题;

• ②两个Packet的buf引用不同的数据缓存空间,每个 Packet都有数据缓存空间的copy;

avpacket和avframe设计

  1. AVFrame 结构体代表一个音频或视频帧
  2. • data:解码后的图像像素数据(音频采样数据)
  3. • linesize:对视频来说是图像中一行像素的大小;对音频来说是整个音频帧的大小
  4. • width, height:图像的宽高(只针对视频)
  5. • key_frame:是否为关键帧(只针对视频) 。
  6. • pict_type:帧类型(只针对视频) 。例如I, P, B
  7. • sample_rate:音频采样率(只针对音频)
  8. • nb_samples:该音频帧中包含的采样数量
  9. • pts:显示时间戳
  10. • channel_layout: 音频数据的通道布局,单声道音频只有一个声道,
  11. 即左右声道完全相同。立体声(双声道)音频有两个独立的声道,分别对应左声道和右声道。
  12. • format:对于视频帧:视频帧的像素格式,例如 AV_PIX_FMT_RGB24、AV_PIX_FMT_YUV420P 等。
  13. 对于音频帧:表示该音频帧的采样格式,例如 AV_SAMPLE_FMT_S16、AV_SAMPLE_FMT_FLT 等。
  14. AVPacket 结构体代表一个压缩的音频或视频帧
  15. • pts:显示时间戳
  16. • dts:解码时间戳
  17. • data:压缩编码数据
  18. • size:压缩编码数据大小
  19. • pos:数据的偏移地址
  20. • stream_index:所属的AVStream

常用API

AVPacket 

  1. AVPacket *av_packet_alloc(void);
  2. malloc分配AVPacket对象 这个时候buffer并没malloc
  3. void av_packet_free(AVPacket **pkt);
  4. 释放AVPacket 和_alloc对应
  5. void av_init_packet(AVPacket *pkt);
  6. 初始化AVPacket字段为null,不分配内存
  7. int av_new_packet(AVPacket *pkt, int size);
  8. buff分配内存,并初始化pkt,引用计数初始化为1 所有不能再调用init了 导致buffer指针丢失
  9. int av_packet_ref(AVPacket *dst, const AVPacket *src);
  10. 增加引用计数 多次ref如果没有对应多次unref将会内存泄漏
  11. void av_packet_unref(AVPacket *pkt);
  12. 清空pkt pack->buff 引用计数为0释放 并调用init
  13. void av_packet_move_ref(AVPacket *dst, AVPacket *src);
  14. 转移引用计数后init(src) 需要free(src)
  15. AVPacket *av_packet_clone(const AVPacket *src);
  16. 等于 深拷贝 av_packet_alloc()+av_packet_ref()
  17. *pkt2 = *pkt; 语义: void av_packet_move_ref(pkt2, pkt);


AVFrame

与avpacket使用类似

  1. AVFrame *av_frame_alloc(void);
  2. 分配AVFrame
  3. int av_frame_get_buffer(AVFrame *frame, int align);
  4. 根据AVFrame分配内存
  5. void av_frame_free(AVFrame **frame);
  6. 释放AVFrame
  7. int av_frame_ref(AVFrame *dst, const AVFrame *src);
  8. 增加引用计数
  9. void av_frame_unref(AVFrame *frame); 减少引用计数
  10. void av_frame_move_ref(AVFrame *dst, AVFrame *src);
  11. 转移引用计数
  12. AVFrame *av_frame_clone(const AVFrame *src);     
  13. 等于av_frame_alloc() + av_frame_ref()
  14. int av_frame_make_writable(AVFrame *frame);  
  15. 用来确保一个 AVFrame 是独立且可以被安全地写入的。如果 AVFrame 只有一个引用,
  16. 那么直接返回 AVFrame 本身。如果 AVFrame 有多个引用,那么它会创建一个新的独立的
  17. AVFrame,并将数据拷贝到新的 AVFrame 中。
  18. 这样做的目的是为了防止多个引用同时修改 AVFrame 的数据缓冲区,从而引发数据竞争和崩溃问题。

图解 

avpacket使用坑点 内存泄漏 

多次ref调用

  1. void av_packet_test5()
  2. {
  3. AVPacket *pkt = NULL;
  4. AVPacket *pkt2 = NULL;
  5. int ret = 0;
  6. pkt = av_packet_alloc(); //
  7. ret = av_new_packet(pkt, MEM_ITEM_SIZE);
  8. memccpy(pkt->data, (void *)&av_packet_test1, 1, MEM_ITEM_SIZE);
  9. pkt2 = av_packet_alloc(); // 必须先alloc
  10. av_packet_move_ref(pkt2, pkt); // av_packet_move_ref
  11. av_packet_ref(pkt, pkt2);
  12. av_packet_ref(pkt, pkt2); // 多次ref如果没有对应多次unref将会内存泄漏
  13. if(pkt->buf) // 打印referenc-counted,必须保证传入的是有效指针
  14. { printf("%s(%d) ref_count(pkt) = %d\n", __FUNCTION__, __LINE__,av_buffer_get_ref_count(pkt->buf)); //3
  15. }
  16. if(pkt2->buf) // 打印referenc-counted,必须保证传入的是有效指针
  17. { printf("%s(%d) ref_count(pkt) = %d\n", __FUNCTION__, __LINE__,
  18. av_buffer_get_ref_count(pkt2->buf));//3
  19. }
  20. av_packet_unref(pkt); // 将为2
  21. av_packet_unref(pkt); // 做第二次是没有用的
  22. if(pkt->buf)
  23. printf("pkt->buf没有被置NULL\n");
  24. else
  25. printf("pkt->buf已经被置NULL\n"); //执行
  26. if(pkt2->buf) // 打印referenc-counted,必须保证传入的是有效指针
  27. { printf("%s(%d) ref_count(pkt) = %d\n", __FUNCTION__, __LINE__,
  28. av_buffer_get_ref_count(pkt2->buf)); //2
  29. }
  30. av_packet_unref(pkt2); //1 而pkt和pkt2 都已经解除引用 但是引用计数还是1 这就导致buff内存泄漏了。
  31. av_packet_free(&pkt);
  32. av_packet_free(&pkt2);
  33. }

init乱用

  1. void av_packet_test4()
  2. {
  3. AVPacket *pkt = NULL;
  4. // av_packet_alloc()没有必要,因为av_packet_clone内部有调用 av_packet_alloc
  5. AVPacket *pkt2 = NULL;
  6. int ret = 0;
  7. pkt = av_packet_alloc();
  8. ret = av_new_packet(pkt, MEM_ITEM_SIZE);
  9. memccpy(pkt->data, (void *)&av_packet_test1, 1, MEM_ITEM_SIZE);
  10. pkt2 = av_packet_clone(pkt); // av_packet_alloc()+av_packet_ref(), 调用该函数后,pkt和pkt2对应的buf引用计数变成2
  11. av_init_packet(pkt); // 这里是故意去做init的操作,让这个函数出现内存泄漏
  12. av_packet_free(&pkt); // pkt在调用av_init_packet后,对应的buff被置为NULL,在调用av_packet_free没法做引用计数-1的操作
  13. av_packet_free(&pkt2); // 触发引用计数变为1,但因为不是0,所以buff不会被释放,导致内存泄漏
  14. }

avframe使用

  1. void av_frame_test1()
  2. {
  3. AVFrame *frame = NULL;
  4. int ret = 0;
  5. frame = av_frame_alloc();// 没有类似的AVPacket的av_new_packet的API
  6. // 1024 *2 * (16/8) =
  7. frame->nb_samples = 1024;
  8. //AV_SAMPLE_FMT_S16P 非交错 AV_SAMPLE_FMT_S16 交错
  9. frame->format = AV_SAMPLE_FMT_S16P;
  10. //AV_CH_LAYOUT_MONO 单声道 AV_CH_LAYOUT_STEREO 立体声
  11. frame->channel_layout = AV_CH_LAYOUT_STEREO;
  12. ret = av_frame_get_buffer(frame, 0); // 根据格式分配内存
  13. {
  14. if(frame->buf && frame->buf[0])
  15. 36 printf("%s(%d) 1 frame->buf[0]->size = %d\n", __FUNCTION__, __LINE__, frame->buf[0]->size); //受frame->format等参数影响
  16. if(frame->buf && frame->buf[1])
  17. 38 printf("%s(%d) 1 frame->buf[1]->size = %d\n", __FUNCTION__, __LINE__, frame->buf[1]->size); //受frame->format等参数影响
  18. }
  19. if(frame->buf && frame->buf[0]) // 打印referenc-counted,必须保证传入的是有效指针
  20. 42 printf("%s(%d) ref_count1(frame) = %d\n", __FUNCTION__, __LINE__, av_buffer_get_ref_count(frame->buf[0]));
  21. // ref_count1(frame) = 1
  22. ret = av_frame_make_writable(frame); // 当frame本身为空时不能make writable
  23. printf("av_frame_make_writable ret = %d\n", ret);
  24. 49 if(frame->buf && frame->buf[0]) // 打印referenc-counted,必须保证传入的是有效指针
  25. printf("%s(%d) ref_count2(frame) = %d\n", __FUNCTION__, __LINE__
  26. , av_buffer_get_ref_count(frame->buf[0]));
  27. av_frame_unref(frame);
  28. if(frame->buf && frame->buf[0]) // 打印referenc-counted,必须保证传入的是有效指针
  29. printf("%s(%d) ref_count3(frame) = %d\n", __FUNCTION__, __LINE__
  30. , av_buffer_get_ref_count(frame->buf[0]));
  31. av_frame_free(&frame);
  32. }
  1. av_frame_test1(36) 1 frame->buf[0]->size = 2048
  2. av_frame_test1(38) 1 frame->buf[1]->size = 2048
  3. av_frame_test1(42) ref_count1(frame) = 1
  4. av_frame_make_writable ret = 0
  5. av_frame_test1(49) ref_count2(frame) = 1

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Li_阴宅/article/detail/972725
推荐阅读
相关标签
  

闽ICP备14008679号