Codec2框架采用了全新的Buffer分配机制C2Allocator,这一篇文章我们一起来瞧瞧C2DmaBufAllocator是如何工作的。
1、C2Allocator
C2Allocator声明在C2Buffer.h中,它定义了如下接口:
- getName:返回Allocator独一无二的名称;
- getId:返回Allocator独一无二的id;
- getTraits:返回Allocator的特征;
- newLinearAllocation:使用给定的capacity和usage分配一个1D的Allocation;
- priorLinearAllocation:使用一个native handle创建一个1D的Allocation;
- newGraphicAllocation:使用给定的width,height,format和usage分配一个2D的Allocation;
- priorGraphicAllocation:使用一个native handle创建一个2D的Allocation;
可以发现几个创建Allocation的方法并不是纯虚函数,这是为什么呢?在头文件中有一串注释,翻译过来是这样:理论上Allocator可以同时支持1D和2D Allocation的分配,但是实际上,一个Allocator只会支持1D和2D中的一个。也就是说,C2Allocator接口的实现者会专注1D或2D的其中一个,不被支持的方法可以不用实现。
2、DMA
DMA(Direct Memory Access,直接内存访问)可以简单理解为是一种共享内存机制,具体的概念和工作原理有兴趣的同学可自行搜索,这一节我们简单看下如何分配出DmaBuf。
以下这段代码是笔者从C2DmaBufAllocator.cpp和BufferAllocator.cpp摘录拼凑出来的,编译之后可以成功运行:
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <linux/dma-heap.h>
#include <string.h>
int main() {
// 1
int dma_heap_fd = open("/dev/dma_heap/system", O_RDONLY | O_CLOEXEC);
if (dma_heap_fd < 0) {
perror("open");
exit(1);
}
// 2
struct dma_heap_allocation_data heap_data = {
.len = 4096, // 设置所需的缓冲区长度
.fd_flags = O_RDWR | O_CLOEXEC, // 设置缓冲区的文件描述符标志
};
if (ioctl(dma_heap_fd, DMA_HEAP_IOCTL_ALLOC, &heap_data) < 0) {
perror("ioctl");
close(dma_heap_fd);
exit(1);
}
int dma_buf_fd = heap_data.fd;
// 3
void *dma_buf = mmap(NULL, heap_data.len, PROT_READ | PROT_WRITE, MAP_SHARED, dma_buf_fd, 0);
if (dma_buf == MAP_FAILED) {
perror("mmap");
close(dma_buf_fd);
close(dma_heap_fd);
exit(1);
}
// 4
memset(dma_buf, 0xAA, heap_data.len);
for (int i = 0; i < heap_data.len; i++) {
printf("0x%02x ", ((unsigned char *)dma_buf)[i]);
}
// 5
munmap(dma_buf, heap_data.len);
close(dma_buf_fd);
close(dma_heap_fd);
}
分配使用DmaBuf有如下步骤:
- 打开DMA Heap设备/dev/dma_heap/system;
- 使用ioctl分配DMA缓冲区,返回的fd存在dma_heap_allocation_data中;
- 得到DMA缓冲区的文件描述符后,可以使用mmap将其映射到用户空间;
- 可以直接通过映射出来的dma_buf指针来访问DMA缓冲区;
- 使用完缓冲区之后要清理资源。
3、C2DmaBufAllocator
先来看C2DmaBufAllocator的创建过程: