如何从单个文件描述符分配多个MMAP?

3
因为我的毕业设计,我正在使用Video4Linux2从相机中获取YUV420图像,并将它们解析到x264中(x264本身可以处理这些图像),然后通过Live555将编码后的流发送到无线网络上的客户端RTP / RTCP兼容的视频播放器。我试图在实时情况下完成所有这些操作,因此需要一个控制算法,但这不是本问题的范围。除了Live555之外,所有内容都是用C语言编写的。目前,我已接近完成视频编码,但想要提高性能。
说实话,我遇到了一些问题... 我试图避免使用V4L2的用户空间指针,而是使用mmap()。我正在编码视频,但由于它是YUV420格式,我一直在malloc新内存来保存Y'、U和V平面,以便供x264读取。我希望将这些变量保留为指向mmap的内存块的指针。
然而,V4L2设备只有一个文件描述符用于缓冲流,我需要将流分成三个符合YUV420标准的mmap变量,如下所示...
buffers[n_buffers].y_plane = mmap(NULL, (2 * width * height) / 3,
                                    PROT_READ | PROT_WRITE, MAP_SHARED,
                                    fd, buf.m.offset);
buffers[n_buffers].u_plane = mmap(NULL, width * height / 6,
                                    PROT_READ | PROT_WRITE, MAP_SHARED,
                                    fd, buf.m.offset +
                                    ((2 * width * height) / 3 + 1) /
                                    sysconf(_SC_PAGE_SIZE));
buffers[n_buffers].v_plane = mmap(NULL, width * height / 6,
                                    PROT_READ | PROT_WRITE, MAP_SHARED,
                                    fd, buf.m.offset +
                                    ((2 * width * height) / 3 + 
                                    width * height / 6 + 1) / 
                                    sysconf(_SC_PAGE_SIZE));

"width"和"height"是视频的分辨率(例如640x480)。

据我了解,MMAP会像这样(伪代码)遍历文件:

fd = v4l2_open(...);
lseek(fd, buf.m.offset + (2 * width * height) / 3);
read(fd, buffers[n_buffers].u_plane, width * height / 6);

我的代码存放在这里的Launchpad Repo(了解更多背景): http://bazaar.launchpad.net/~alex-stevens/+junk/spyPanda/files (修订版11)

YUV420格式可以从此Wiki示意图清楚地看到: http://en.wikipedia.org/wiki/File:Yuv420.svg(我希望将Y、U和V字节分别拆分到每个mmap'ed内存中)

有人能解释一下如何从一个文件描述符中映射三个变量到内存,或者为什么我错了吗?或者甚至提示一个更好的想法来解析YUV420缓冲区到x264?:P

干杯!^^

2个回答

3
不需要三个单独的 mmap。只需一次性进行 mmap,然后计算每个平面的基指针相对于整个映射的基指针。 编辑:您需要像这样的东西:
unsigned char *y = mmap(...); /* map total size of all 3 planes */
unsigned char *u = y + y_height*y_bytes_per_line;
unsigned char *v = u + u_height*u_bytes_per_line;

那么,我该如何拥有三个基指针,指向mmap内的特定大小分区,而不需要将它们复制到另一块内存中? - Alex Stevens
好的,我现在也会尝试一下。我使用了一个带有特定Y、U和V数组大小的结构体来类型转换我的mmap(就像zvrba帖子中的注释所示)。这似乎可以工作,虽然有点hack,但你的方法可能更好。 - Alex Stevens
使用结构体并不是非常灵活的,因为它不允许您支持不同的图片尺寸/分辨率。自己进行指针算术运算会更加灵活。 - R.. GitHub STOP HELPING ICE
万岁!这个也完美地运作了!:P 我绝对理解你的意思 ^^ - Alex Stevens

1
如果我理解你的意思:你不能将mmap“非交错”顺序内存转换为单独的指针。您必须映射单个内存块,并手动计算缓冲区内的适当偏移量?
(但是:没有任何理由不能多次映射相同的fd。您从未说过出了什么问题。)

好的,那么你能将映射的内存转换为一个结构体,例如:typedef struct { void y_plane[250]; void u_plane[125]; void v_plane[125] } YuvFormat; 这样你就可以从单个mmap指向特定的内存范围了吗? - Alex Stevens
其实...那个方法行得通!现在只需要计算YUV420流中Y、U和V的长度就可以了!干杯 :P - Alex Stevens

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接