Bug 1876895 - WebRTC backport: Video capture PipeWire: add support for DMABuf buffer type r=pehrsons,webrtc-reviewers

This is a simple backport of an WebRTC upstream change.

Upstream commit: 334e9133dcdecb5d00d991332e05c7b80ae26578

Differential Revision: https://phabricator.services.mozilla.com/D202929
This commit is contained in:
Jan Grulich 2024-03-05 08:38:18 +00:00
parent 3da8bca73f
commit 99133f08ff
4 changed files with 111 additions and 39 deletions

View File

@ -14,7 +14,6 @@
#include <libdrm/drm_fourcc.h>
#include <pipewire/pipewire.h>
#include <spa/param/video/format-utils.h>
#include <sys/mman.h>
#include <vector>
@ -49,33 +48,6 @@ constexpr int CursorMetaSize(int w, int h) {
constexpr PipeWireVersion kDmaBufModifierMinVersion = {0, 3, 33};
constexpr PipeWireVersion kDropSingleModifierMinVersion = {0, 3, 40};
class ScopedBuf {
public:
ScopedBuf() {}
ScopedBuf(uint8_t* map, int map_size, int fd)
: map_(map), map_size_(map_size), fd_(fd) {}
~ScopedBuf() {
if (map_ != MAP_FAILED) {
munmap(map_, map_size_);
}
}
explicit operator bool() { return map_ != MAP_FAILED; }
void initialize(uint8_t* map, int map_size, int fd) {
map_ = map;
map_size_ = map_size;
fd_ = fd;
}
uint8_t* get() { return map_; }
protected:
uint8_t* map_ = static_cast<uint8_t*>(MAP_FAILED);
int map_size_;
int fd_;
};
class SharedScreenCastStreamPrivate {
public:
SharedScreenCastStreamPrivate();

View File

@ -11,6 +11,21 @@
#ifndef MODULES_PORTAL_PIPEWIRE_UTILS_H_
#define MODULES_PORTAL_PIPEWIRE_UTILS_H_
#include <errno.h>
#include <stdint.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
// static
struct dma_buf_sync {
uint64_t flags;
};
#define DMA_BUF_SYNC_READ (1 << 0)
#define DMA_BUF_SYNC_START (0 << 2)
#define DMA_BUF_SYNC_END (1 << 2)
#define DMA_BUF_BASE 'b'
#define DMA_BUF_IOCTL_SYNC _IOW(DMA_BUF_BASE, 0, struct dma_buf_sync)
struct pw_thread_loop;
namespace webrtc {
@ -32,6 +47,66 @@ class PipeWireThreadLoopLock {
pw_thread_loop* const loop_;
};
// We should synchronize DMA Buffer object access from CPU to avoid potential
// cache incoherency and data loss.
// See
// https://01.org/linuxgraphics/gfx-docs/drm/driver-api/dma-buf.html#cpu-access-to-dma-buffer-objects
static bool SyncDmaBuf(int fd, uint64_t start_or_end) {
struct dma_buf_sync sync = {0};
sync.flags = start_or_end | DMA_BUF_SYNC_READ;
while (true) {
int ret;
ret = ioctl(fd, DMA_BUF_IOCTL_SYNC, &sync);
if (ret == -1 && errno == EINTR) {
continue;
} else if (ret == -1) {
return false;
} else {
break;
}
}
return true;
}
class ScopedBuf {
public:
ScopedBuf() {}
ScopedBuf(uint8_t* map, int map_size, int fd, bool is_dma_buf = false)
: map_(map), map_size_(map_size), fd_(fd), is_dma_buf_(is_dma_buf) {}
~ScopedBuf() {
if (map_ != MAP_FAILED) {
if (is_dma_buf_) {
SyncDmaBuf(fd_, DMA_BUF_SYNC_END);
}
munmap(map_, map_size_);
}
}
explicit operator bool() { return map_ != MAP_FAILED; }
void initialize(uint8_t* map, int map_size, int fd, bool is_dma_buf = false) {
map_ = map;
map_size_ = map_size;
is_dma_buf_ = is_dma_buf;
fd_ = fd;
if (is_dma_buf_) {
SyncDmaBuf(fd_, DMA_BUF_SYNC_START);
}
}
uint8_t* get() { return map_; }
protected:
uint8_t* map_ = static_cast<uint8_t*>(MAP_FAILED);
int map_size_;
int fd_;
bool is_dma_buf_;
};
} // namespace webrtc
#endif // MODULES_PORTAL_PIPEWIRE_UTILS_H_

View File

@ -178,8 +178,7 @@ int32_t VideoCaptureModulePipeWire::StartCapture(
int res = pw_stream_connect(
stream_, PW_DIRECTION_INPUT, node_id_,
static_cast<enum pw_stream_flags>(PW_STREAM_FLAG_AUTOCONNECT |
PW_STREAM_FLAG_DONT_RECONNECT |
PW_STREAM_FLAG_MAP_BUFFERS),
PW_STREAM_FLAG_DONT_RECONNECT),
params.data(), params.size());
if (res != 0) {
RTC_LOG(LS_ERROR) << "Could not connect to camera stream: "
@ -312,11 +311,11 @@ void VideoCaptureModulePipeWire::OnFormatChanged(const struct spa_pod* format) {
0);
}
const int buffer_types =
(1 << SPA_DATA_DmaBuf) | (1 << SPA_DATA_MemFd) | (1 << SPA_DATA_MemPtr);
spa_pod_builder_add(
&builder, SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(8, 1, 32),
SPA_PARAM_BUFFERS_dataType,
SPA_POD_CHOICE_FLAGS_Int((1 << SPA_DATA_MemFd) | (1 << SPA_DATA_MemPtr)),
0);
SPA_PARAM_BUFFERS_dataType, SPA_POD_CHOICE_FLAGS_Int(buffer_types), 0);
params.push_back(
static_cast<spa_pod*>(spa_pod_builder_pop(&builder, &frame)));
@ -384,14 +383,15 @@ void VideoCaptureModulePipeWire::ProcessBuffers() {
RTC_CHECK_RUNS_SERIALIZED(&capture_checker_);
while (pw_buffer* buffer = pw_stream_dequeue_buffer(stream_)) {
spa_buffer* spaBuffer = buffer->buffer;
struct spa_meta_header* h;
h = static_cast<struct spa_meta_header*>(
spa_buffer_find_meta_data(buffer->buffer, SPA_META_Header, sizeof(*h)));
spa_buffer_find_meta_data(spaBuffer, SPA_META_Header, sizeof(*h)));
struct spa_meta_videotransform* videotransform;
videotransform =
static_cast<struct spa_meta_videotransform*>(spa_buffer_find_meta_data(
buffer->buffer, SPA_META_VideoTransform, sizeof(*videotransform)));
spaBuffer, SPA_META_VideoTransform, sizeof(*videotransform)));
if (videotransform) {
VideoRotation rotation =
VideorotationFromPipeWireTransform(videotransform->transform);
@ -401,11 +401,35 @@ void VideoCaptureModulePipeWire::ProcessBuffers() {
if (h->flags & SPA_META_HEADER_FLAG_CORRUPTED) {
RTC_LOG(LS_INFO) << "Dropping corruped frame.";
} else {
IncomingFrame(static_cast<unsigned char*>(buffer->buffer->datas[0].data),
buffer->buffer->datas[0].chunk->size,
configured_capability_);
pw_stream_queue_buffer(stream_, buffer);
continue;
}
if (spaBuffer->datas[0].type == SPA_DATA_DmaBuf ||
spaBuffer->datas[0].type == SPA_DATA_MemFd) {
ScopedBuf frame;
frame.initialize(
static_cast<uint8_t*>(
mmap(nullptr,
spaBuffer->datas[0].maxsize + spaBuffer->datas[0].mapoffset,
PROT_READ, MAP_PRIVATE, spaBuffer->datas[0].fd, 0)),
spaBuffer->datas[0].maxsize + spaBuffer->datas[0].mapoffset,
spaBuffer->datas[0].fd, spaBuffer->datas[0].type == SPA_DATA_DmaBuf);
if (!frame) {
RTC_LOG(LS_ERROR) << "Failed to mmap the memory: "
<< std::strerror(errno);
return;
}
IncomingFrame(
SPA_MEMBER(frame.get(), spaBuffer->datas[0].mapoffset, uint8_t),
spaBuffer->datas[0].chunk->size, configured_capability_);
} else { // SPA_DATA_MemPtr
IncomingFrame(static_cast<uint8_t*>(spaBuffer->datas[0].data),
spaBuffer->datas[0].chunk->size, configured_capability_);
}
pw_stream_queue_buffer(stream_, buffer);
}
}

View File

@ -0,0 +1 @@
We cherry-picked this in bug 1876895.