Der Autor veröffentlicht udmabuf in Open Source.
Dieser Artikel beschreibt, wie der von udmabuf zugewiesene Puffer als V4L2-Puffer verwendet wird.
Im nächsten Artikel habe ich das Problem gezeigt, dass der Cache nicht aktiviert ist, wenn der V4L2-Puffer mit dem V4L2-Treiber unter Verwendung von Xilinx VDMA usw. mmapiert wird.
Als Gegenmaßnahme können Sie anstelle von V4L2_MEMORY_MMAP einen Puffer auf der Seite der Benutzeranwendung zuweisen und ihn mit V4L2_MEMORY_USERPTR an den V4L2-Treiber übergeben. Die Xilinx-Foren haben jedoch gezeigt, dass diese Methode nicht funktioniert.
Dies ist offensichtlich, wenn Sie darüber nachdenken. Da Xilinx VDMA Scatter Gather nicht unterstützt, muss der V4L2-Puffer im physischen Speicher zusammenhängend sein. Wenn Sie einen Puffer im Benutzerbereich zuweisen, gibt es keine Garantie dafür, dass er im physischen Speicher zusammenhängend ist. Ich steckte in diesem Scheck fest.
Daher wurde versucht, mit udmabuf einen zusammenhängenden Puffer im physischen Speicher zu sichern, diesen Puffer mit mmap dem Benutzerbereich zuzuordnen und ihn mit V4L2_MEMORY_USERPTR an den V4L2-Treiber zu übergeben. Leider hat es zuerst nicht funktioniert und ich habe udmabuf ein Problem gegeben.
Am Ende scheint dieser Versuch erfolgreich gewesen zu sein. Es dauerte 24 ms, bis V4L2_MEMORY_MMAP NV12-Bilddaten mit 1920 x 1080p übertragen hatte, für udmabuf + V4L2_MEMORY_USERPTR jedoch 1,6 ms.
Hier erklären wir anhand eines Beispiels, wie der von udmabuf zugewiesene Puffer als V4L2-Puffer verwendet wird.
udmabuf ist ein Linux-Kernelmodul. Informationen zum Erstellen und Installieren von udmabuf finden Sie in den Materialien wie Readm.md von udmabuf.
Öffnen Sie udmabuf.
udmabuf.hpp
name = device_name;
sprintf(file_name, "/dev/%s", name.c_str());
if ((_device_file = open(file_name, O_RDWR)) < 0) {
printf("Can not open %s\\n", file_name);
_status = false;
goto done;
}
Ordnen Sie udmabuf mit mmap dem Benutzerbereich zu.
udmabuf.hpp
buf = mmap(NULL, buf_size, PROT_READ|PROT_WRITE, MAP_SHARED, _device_file, 0);
if (buf == MAP_FAILED) {
printf("Can not mmap %s\\n", file_name);
_status = false;
goto done;
}
Eigentlich ist dieser Prozess sehr wichtig und unverzichtbar. Dies liegt daran, dass udmabuf den physischen Speicher durch On-Demand-Paging virtuellen Adressen zuordnet. Um etwas näher darauf einzugehen, ordnet udmabuf den physischen Speicher mit mmap () nicht sofort virtuellen Adressen zu. Wenn Sie auf eine virtuelle Adresse zugreifen, tritt ein Seitenfehler auf, wenn der physische Speicher nicht zugeordnet ist. udmabuf ordnet den physischen Speicher nur zu, wenn ein Seitenfehler auftritt.
Im Fall von udmabuf wurde die von mmap () zurückgegebene virtuelle Adresse noch keiner physischen Adresse zugeordnet. Wenn Sie diese virtuelle Adresse also an ein V4L2-Gerät übergeben, wie in [Puffer von UDMABUF und V4L2_MEMORY_USERPTR # 38] gezeigt] Fehler tritt auf.
Um dies zu verhindern, empfiehlt es sich, den Puffer unmittelbar nach mMap () zu löschen. Durch das Schreiben in den Puffer wird ein Seitenfehler verursacht und der physische Speicher der virtuellen Adresse zugeordnet.
udmabuf.hpp
// Write once to allocate physical memory to u-dma-buf virtual space.
// Note: Do not use memset() for this.
// Because it does not work as expected.
{
uint64_t* word_ptr = reinterpret_cast<uint64_t*>(buf);
size_t words = buf_size/sizeof(uint64_t);
for(int i = 0 ; i < words; i++) {
word_ptr[i] = 0;
}
}
Verwenden Sie memset () nicht, um den Puffer zu löschen. memset () macht sehr komplizierte Dinge und ich kann nicht die gewünschten Ergebnisse erzielen.
In Zukunft überlegen wir, dies in udmabuf zu tun. Löschen Sie bis dahin den Puffer explizit wie in diesem Anwendungsfall.
Fordern Sie einen V4L2-Puffer vom V4L2-Treiber an. Geben Sie dabei V4L2_MEMORY_USERPTR für req.memory an, um anzugeben, dass der V4L2-Puffer vom Benutzerprogramm anstatt vom V4L2-Treiber zugewiesen werden soll.
example.cpp
v4l2_requestbuffers req ;
memset(&req, 0, sizeof(req));
req.count = num_buf;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_USERPTR;
ret = xioctl(fd, VIDIOC_REQBUFS, &req);
if (ret < 0) {
perror("ioctl(VIDIOC_REQBUFS)");
return -1;
}
Stellen Sie den V4L2-Puffer vor dem Starten des Streamings in die Warteschlange des V4L2-Treibers. Die virtuelle Adresse, die den physischen Speicher des von udmabuf zugewiesenen Puffers abbildet, wird in buf.m.userptr festgelegt.
example.cpp
for (i = 0; i < num_buf; i++) {
v4l2_buffer buf;
memset(&buf, 0, sizeof(buf));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_USERPTR;
buf.index = i;
buf.length = buf_size;
buf.m.userptr = reinterpret_cast<unsigned long>(udmabuf.buf + i*buf_size);
ret = xioctl(fd, VIDIOC_QBUF, &buf);
if (ret < 0) {
perror("ioctl(VIDIOC_QBUF)");
return -1;
}
}
["Gerätetreiber für Programme, die unter Linux im Benutzerbereich ausgeführt werden, und Hardware, die Speicher gemeinsam nutzen" @Qiita]: https://qiita.com/ikwzm/items/cc1bb33ff43a491440ea "" Programme, die unter Linux im Benutzerbereich ausgeführt werden Gerätetreiber für Hardware zur gemeinsamen Nutzung des Speichers mit @Qiita " ["Problem ohne Leistung mit V4L2-Streaming-E / A (V4L2_MEMORY_MMAP)" @Qiita]: https://qiita.com/ikwzm/items/8f1c98efe6fd4ae2490a "Keine Leistung mit V4L2-Streaming-E / A (V4L2_MEMORY_MMAP)" 』@Qiita" [『V4l2 V4L2_MEMORY_USERPTR:contiguous mapping is too small 4096/1228800』]: https://forums.xilinx.com/t5/Embedded-Linux/V4l2-V4L2-MEMORY-USERPTR-contiguous-mapping-is-too-small-4096/td-p/825067 "『V4l2 V4L2_MEMORY_USERPTR:contiguous mapping is too small 4096/1228800』@XilinxForum" [『Buffer from UDMABUF and V4L2_MEMORY_USERPTR #38』]: https://github.com/ikwzm/udmabuf/issues/38 "『Buffer from UDMABUF and V4L2_MEMORY_USERPTR #38』@gitub.com"
Recommended Posts