mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-15 07:00:05 -05:00
Merge branch 'master' into frankk/add_ctrl_to_filter_graph
This commit is contained in:
commit
9b81e7043e
11 changed files with 453 additions and 419 deletions
134
.gitlab-ci.yml
134
.gitlab-ci.yml
|
|
@ -31,6 +31,9 @@ include:
|
|||
- project: 'freedesktop/ci-templates'
|
||||
ref: *templates_sha
|
||||
file: '/templates/alpine.yml'
|
||||
- project: 'freedesktop/ci-templates'
|
||||
ref: *templates_sha
|
||||
file: '/templates/debian.yml'
|
||||
|
||||
.fedora:
|
||||
variables:
|
||||
|
|
@ -102,36 +105,6 @@ include:
|
|||
# FDO_DISTRIBUTION_EXEC: >-
|
||||
# pip3 install meson
|
||||
|
||||
# This is a pruned down container with enough dependencies for a basic i686
|
||||
# build to make sure we've not broken anything. This can be extended if we want
|
||||
# to cover more of the code.
|
||||
.fedora_x86:
|
||||
variables:
|
||||
# Update this tag when you want to trigger a rebuild
|
||||
FDO_DISTRIBUTION_TAG: '2025-05-29.1'
|
||||
FDO_DISTRIBUTION_VERSION: '42'
|
||||
FDO_DISTRIBUTION_PACKAGES: >-
|
||||
git
|
||||
gcc
|
||||
gcc-c++
|
||||
meson
|
||||
glibc-devel.i686
|
||||
systemd-devel.i686
|
||||
dbus-devel.i686
|
||||
alsa-lib-devel.i686
|
||||
bluez-libs-devel.i686
|
||||
libffi-devel.i686
|
||||
pcre2-devel.i686
|
||||
sysprof-devel.i686
|
||||
zlib-ng-compat-devel.i686
|
||||
libblkid-devel.i686
|
||||
libmount-devel.i686
|
||||
libselinux-devel.i686
|
||||
glib2-devel.i686
|
||||
alsa-lib-devel
|
||||
avahi-devel
|
||||
bluez-libs-devel
|
||||
|
||||
.ubuntu:
|
||||
variables:
|
||||
# Update this tag when you want to trigger a rebuild
|
||||
|
|
@ -171,6 +144,30 @@ include:
|
|||
# FDO_DISTRIBUTION_EXEC: >-
|
||||
# pip3 install meson
|
||||
|
||||
.debian:
|
||||
variables:
|
||||
# Update this tag when you want to trigger a rebuild
|
||||
BASE_TAG: '2025-08-10.0'
|
||||
FDO_DISTRIBUTION_VERSION: 'trixie'
|
||||
FDO_DISTRIBUTION_PACKAGES: >-
|
||||
build-essential
|
||||
dpkg-dev
|
||||
findutils
|
||||
git
|
||||
meson
|
||||
|
||||
.debian-archictectures:
|
||||
parallel:
|
||||
matrix:
|
||||
- ARCH:
|
||||
- amd64
|
||||
- arm64
|
||||
- armhf
|
||||
- i386
|
||||
- ppc64el
|
||||
- riscv64
|
||||
- s390x
|
||||
|
||||
.alpine:
|
||||
variables:
|
||||
# Update this tag when you want to trigger a rebuild
|
||||
|
|
@ -243,14 +240,8 @@ include:
|
|||
- echo "Building with meson options $MESON_OPTIONS"
|
||||
- meson setup "$BUILD_DIR" --prefix="$PREFIX" $MESON_OPTIONS
|
||||
- meson compile -C "$BUILD_DIR" $COMPILE_ARGS
|
||||
- |
|
||||
if [ -z "$MESON_SKIP_TEST" ]; then
|
||||
meson test -C "$BUILD_DIR" --no-rebuild
|
||||
fi
|
||||
- |
|
||||
if [ -z "$MESON_SKIP_INSTALL" ]; then
|
||||
meson install -C "$BUILD_DIR" --no-rebuild
|
||||
fi
|
||||
- meson test -C "$BUILD_DIR" --no-rebuild
|
||||
- meson install -C "$BUILD_DIR" --no-rebuild
|
||||
artifacts:
|
||||
name: pipewire-$CI_COMMIT_SHA
|
||||
when: always
|
||||
|
|
@ -265,17 +256,21 @@ container_ubuntu:
|
|||
variables:
|
||||
GIT_STRATEGY: none # no need to pull the whole tree for rebuilding the image
|
||||
|
||||
container_fedora:
|
||||
container_debian:
|
||||
extends:
|
||||
- .fedora
|
||||
- .fdo.container-build@fedora
|
||||
- .debian
|
||||
- .debian-archictectures
|
||||
- .fdo.container-build@debian
|
||||
stage: container
|
||||
variables:
|
||||
GIT_STRATEGY: none # no need to pull the whole tree for rebuilding the image
|
||||
FDO_DISTRIBUTION_TAG: "$BASE_TAG-$ARCH"
|
||||
FDO_DISTRIBUTION_EXEC: >-
|
||||
./.gitlab/ci/setup-debian-cross-container.sh "$ARCH"
|
||||
|
||||
container_fedora_x86:
|
||||
container_fedora:
|
||||
extends:
|
||||
- .fedora_x86
|
||||
- .fedora
|
||||
- .fdo.container-build@fedora
|
||||
stage: container
|
||||
variables:
|
||||
|
|
@ -311,6 +306,44 @@ build_on_ubuntu:
|
|||
variables:
|
||||
MESON_OPTIONS: "-Dsession-managers=[] -Dsnap=enabled"
|
||||
|
||||
build_on_debian:
|
||||
extends:
|
||||
- .debian
|
||||
- .debian-archictectures
|
||||
- .not_coverity
|
||||
- .fdo.distribution-image@debian
|
||||
- .build
|
||||
stage: build
|
||||
needs:
|
||||
- job: container_debian
|
||||
artifacts: false
|
||||
# ideally
|
||||
# parallel:
|
||||
# matrix:
|
||||
# - ARCH: "$ARCH"
|
||||
# however https://gitlab.com/gitlab-org/gitlab/-/issues/423553
|
||||
# ("Expand variables in `needs:parallel:matrix`")
|
||||
variables:
|
||||
FDO_DISTRIBUTION_TAG: "$BASE_TAG-$ARCH"
|
||||
# see /.gitlab/ci/setup-debian-cross-container.sh for installed packages
|
||||
MESON_OPTIONS: >-
|
||||
--cross-file /opt/meson-$ARCH.cross
|
||||
-D c_args=['-UFASTPATH']
|
||||
-D cpp_args=['-UFASTPATH']
|
||||
-D auto_features=enabled
|
||||
-D session-managers=[]
|
||||
-D bluez5-backend-native-mm=enabled
|
||||
-D bluez5-codec-lc3plus=disabled
|
||||
-D bluez5-codec-ldac=disabled
|
||||
-D bluez5-codec-ldac-dec=disabled
|
||||
-D libcamera=disabled
|
||||
-D roc=disabled
|
||||
-D snap=disabled
|
||||
-D systemd-user-service=disabled
|
||||
-D systemd-system-service=disabled
|
||||
-D onnxruntime=disabled
|
||||
-D vulkan=enabled
|
||||
|
||||
.build_on_fedora:
|
||||
extends:
|
||||
- .fedora
|
||||
|
|
@ -406,21 +439,6 @@ build_on_fedora_html_docs:
|
|||
rules:
|
||||
- !reference [pages, rules]
|
||||
|
||||
build_on_fedora_x86:
|
||||
extends:
|
||||
- .fedora_x86
|
||||
- .not_coverity
|
||||
- .fdo.distribution-image@fedora
|
||||
- .build
|
||||
stage: build
|
||||
needs:
|
||||
- job: container_fedora_x86
|
||||
artifacts: false
|
||||
variables:
|
||||
MESON_OPTIONS: "--cross-file=cross-x86.txt"
|
||||
MESON_SKIP_TEST: "true"
|
||||
MESON_SKIP_INSTALL: "true"
|
||||
|
||||
build_on_alpine:
|
||||
extends:
|
||||
- .alpine
|
||||
|
|
|
|||
63
.gitlab/ci/setup-debian-cross-container.sh
Executable file
63
.gitlab/ci/setup-debian-cross-container.sh
Executable file
|
|
@ -0,0 +1,63 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -ex
|
||||
|
||||
packages=(
|
||||
# libapparmor-dev
|
||||
libasound2-dev
|
||||
libavahi-client-dev
|
||||
libavcodec-dev
|
||||
libavfilter-dev
|
||||
libavformat-dev
|
||||
libbluetooth-dev
|
||||
libcanberra-dev
|
||||
libdbus-1-dev
|
||||
libebur128-dev
|
||||
libfdk-aac-dev
|
||||
libffado-dev
|
||||
libfftw3-dev
|
||||
libfreeaptx-dev
|
||||
libglib2.0-dev
|
||||
libgstreamer1.0-dev
|
||||
libgstreamer-plugins-base1.0-dev
|
||||
libjack-jackd2-dev
|
||||
liblc3-dev
|
||||
liblilv-dev
|
||||
libmysofa-dev
|
||||
libopus-dev
|
||||
libpulse-dev
|
||||
libreadline-dev
|
||||
libsbc-dev
|
||||
libsdl2-dev
|
||||
# libsnapd-glib-dev
|
||||
libsndfile1-dev
|
||||
libspandsp-dev
|
||||
libssl-dev
|
||||
libsystemd-dev
|
||||
libudev-dev
|
||||
libusb-1.0-0-dev
|
||||
libvulkan-dev
|
||||
libwebrtc-audio-processing-dev
|
||||
libx11-dev
|
||||
modemmanager-dev
|
||||
)
|
||||
|
||||
arch="$1"
|
||||
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
sed -i \
|
||||
's/^Components:.*$/Components: main contrib non-free non-free-firmware/' \
|
||||
/etc/apt/sources.list.d/debian.sources
|
||||
|
||||
dpkg --add-architecture "$arch"
|
||||
apt update -y
|
||||
|
||||
pkgs=( "crossbuild-essential-$arch" )
|
||||
for pkg in "${packages[@]}"; do
|
||||
pkgs+=( "$pkg:$arch" )
|
||||
done
|
||||
|
||||
apt install -y "${pkgs[@]}"
|
||||
|
||||
meson env2mfile --cross --debarch "$arch" -o "/opt/meson-$arch.cross"
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
[binaries]
|
||||
c = 'gcc'
|
||||
cpp = 'g++'
|
||||
ld = 'ld'
|
||||
cmake = 'cmake'
|
||||
strip = 'strip'
|
||||
pkg-config = 'pkg-config'
|
||||
|
||||
[properties]
|
||||
pkg_config_libdir = '/usr/lib/pkgconfig'
|
||||
ld_args = '-m elf_i386'
|
||||
|
||||
[built-in options]
|
||||
c_args = '-m32 -msse'
|
||||
c_link_args = '-m32 -msse'
|
||||
cpp_args = '-m32 -msse'
|
||||
cpp_link_args = '-m32 -msse'
|
||||
|
||||
[host_machine]
|
||||
system = 'linux'
|
||||
cpu_family = 'x86'
|
||||
cpu = 'i686'
|
||||
endian = 'little'
|
||||
|
|
@ -381,9 +381,6 @@ libusb_dep = dependency('libusb-1.0', required : get_option('libusb'))
|
|||
summary({'libusb (Bluetooth quirks)': libusb_dep.found()}, bool_yn: true, section: 'Backend')
|
||||
cdata.set('HAVE_LIBUSB', libusb_dep.found())
|
||||
|
||||
cap_lib = dependency('libcap', required : false)
|
||||
cdata.set('HAVE_LIBCAP', cap_lib.found())
|
||||
|
||||
glib2_dep = dependency('glib-2.0', required : get_option('flatpak'))
|
||||
summary({'GLib-2.0 (Flatpak support)': glib2_dep.found()}, bool_yn: true, section: 'Misc dependencies')
|
||||
flatpak_support = glib2_dep.found()
|
||||
|
|
|
|||
|
|
@ -411,12 +411,23 @@ static void volume_sync_stop_timer(struct rfcomm *rfcomm);
|
|||
static void rfcomm_free(struct rfcomm *rfcomm)
|
||||
{
|
||||
struct updated_call *updated_call;
|
||||
struct rfcomm_cmd *cmd;
|
||||
|
||||
spa_list_consume(updated_call, &rfcomm->updated_call_list, link) {
|
||||
spa_list_remove(&updated_call->link);
|
||||
free(updated_call);
|
||||
}
|
||||
|
||||
spa_list_consume(cmd, &rfcomm->cmd_send_queue, link) {
|
||||
if (cmd->msg) {
|
||||
telephony_send_dbus_method_reply(rfcomm->backend->telephony, cmd->msg, BT_TELEPHONY_ERROR_FAILED, 0);
|
||||
spa_clear_ptr(cmd->msg, dbus_message_unref);
|
||||
}
|
||||
|
||||
spa_list_remove(&cmd->link);
|
||||
free(cmd);
|
||||
}
|
||||
|
||||
codec_switch_stop_timer(rfcomm);
|
||||
if (rfcomm->telephony_ag) {
|
||||
telephony_ag_destroy(rfcomm->telephony_ag);
|
||||
|
|
@ -1900,6 +1911,12 @@ static const struct spa_bt_telephony_ag_callbacks telephony_ag_callbacks = {
|
|||
.set_microphone_volume = hfp_hf_set_microphone_volume,
|
||||
};
|
||||
|
||||
#define hfp_hf_set_call_state(log, obj, new_state) \
|
||||
({ \
|
||||
spa_log_debug(log, "call id: %u, %u -> %u", obj->id, obj->state, new_state); \
|
||||
obj->state = new_state; \
|
||||
})
|
||||
|
||||
static void hfp_hf_remove_disconnected_calls(struct rfcomm *rfcomm)
|
||||
{
|
||||
struct impl *backend = rfcomm->backend;
|
||||
|
|
@ -1919,7 +1936,7 @@ static void hfp_hf_remove_disconnected_calls(struct rfcomm *rfcomm)
|
|||
spa_log_debug(backend->log, "call %d -> %s", call->id, found ? "updated" : "disconnected");
|
||||
|
||||
if (!found) {
|
||||
call->state = CALL_STATE_DISCONNECTED;
|
||||
hfp_hf_set_call_state(backend->log, call, CALL_STATE_DISCONNECTED);
|
||||
telephony_call_notify_updated_props(call);
|
||||
telephony_call_destroy(call);
|
||||
}
|
||||
|
|
@ -2046,7 +2063,7 @@ static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token)
|
|||
spa_list_for_each_safe(call, tcall, &rfcomm->telephony_ag->call_list, link) {
|
||||
if (call->state == CALL_STATE_DIALING || call->state == CALL_STATE_ALERTING ||
|
||||
call->state == CALL_STATE_INCOMING || call->state == CALL_STATE_WAITING) {
|
||||
call->state = CALL_STATE_DISCONNECTED;
|
||||
hfp_hf_set_call_state(backend->log, call, CALL_STATE_DISCONNECTED);
|
||||
telephony_call_notify_updated_props(call);
|
||||
telephony_call_destroy(call);
|
||||
}
|
||||
|
|
@ -2090,7 +2107,7 @@ static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token)
|
|||
struct spa_bt_telephony_call *call;
|
||||
spa_list_for_each(call, &rfcomm->telephony_ag->call_list, link) {
|
||||
if (call->state == CALL_STATE_DIALING) {
|
||||
call->state = CALL_STATE_ALERTING;
|
||||
hfp_hf_set_call_state(backend->log, call, CALL_STATE_ALERTING);
|
||||
telephony_call_notify_updated_props(call);
|
||||
}
|
||||
}
|
||||
|
|
@ -2107,7 +2124,7 @@ static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token)
|
|||
struct spa_bt_telephony_call *call, *tcall;
|
||||
spa_list_for_each_safe(call, tcall, &rfcomm->telephony_ag->call_list, link) {
|
||||
if (call->state == CALL_STATE_ACTIVE) {
|
||||
call->state = CALL_STATE_DISCONNECTED;
|
||||
hfp_hf_set_call_state(backend->log, call, CALL_STATE_DISCONNECTED);
|
||||
telephony_call_notify_updated_props(call);
|
||||
telephony_call_destroy(call);
|
||||
}
|
||||
|
|
@ -2117,7 +2134,7 @@ static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token)
|
|||
spa_list_for_each(call, &rfcomm->telephony_ag->call_list, link) {
|
||||
if (call->state == CALL_STATE_DIALING || call->state == CALL_STATE_ALERTING ||
|
||||
call->state == CALL_STATE_INCOMING) {
|
||||
call->state = CALL_STATE_ACTIVE;
|
||||
hfp_hf_set_call_state(backend->log, call, CALL_STATE_ACTIVE);
|
||||
telephony_call_notify_updated_props(call);
|
||||
}
|
||||
}
|
||||
|
|
@ -2135,7 +2152,7 @@ static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token)
|
|||
bool found_waiting = false;
|
||||
spa_list_for_each_safe(call, tcall, &rfcomm->telephony_ag->call_list, link) {
|
||||
if (call->state == CALL_STATE_WAITING) {
|
||||
call->state = CALL_STATE_DISCONNECTED;
|
||||
hfp_hf_set_call_state(backend->log, call, CALL_STATE_DISCONNECTED);
|
||||
telephony_call_notify_updated_props(call);
|
||||
telephony_call_destroy(call);
|
||||
found_waiting = true;
|
||||
|
|
@ -2145,7 +2162,7 @@ static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token)
|
|||
if (!found_waiting) {
|
||||
spa_list_for_each_safe(call, tcall, &rfcomm->telephony_ag->call_list, link) {
|
||||
if (call->state == CALL_STATE_HELD) {
|
||||
call->state = CALL_STATE_DISCONNECTED;
|
||||
hfp_hf_set_call_state(backend->log, call, CALL_STATE_DISCONNECTED);
|
||||
telephony_call_notify_updated_props(call);
|
||||
telephony_call_destroy(call);
|
||||
}
|
||||
|
|
@ -2156,10 +2173,10 @@ static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token)
|
|||
spa_list_for_each(call, &rfcomm->telephony_ag->call_list, link) {
|
||||
bool changed = false;
|
||||
if (call->state == CALL_STATE_ACTIVE) {
|
||||
call->state = CALL_STATE_HELD;
|
||||
hfp_hf_set_call_state(backend->log, call, CALL_STATE_HELD);
|
||||
changed = true;
|
||||
} else if (call->state == CALL_STATE_HELD) {
|
||||
call->state = CALL_STATE_ACTIVE;
|
||||
hfp_hf_set_call_state(backend->log, call, CALL_STATE_ACTIVE);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
|
|
@ -2171,7 +2188,7 @@ static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token)
|
|||
spa_list_for_each(call, &rfcomm->telephony_ag->call_list, link) {
|
||||
bool changed = false;
|
||||
if (call->state == CALL_STATE_ACTIVE || call->state == CALL_STATE_WAITING) {
|
||||
call->state = CALL_STATE_HELD;
|
||||
hfp_hf_set_call_state(backend->log, call, CALL_STATE_HELD);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
|
|
@ -2420,14 +2437,14 @@ static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token)
|
|||
struct spa_bt_telephony_call *call, *tcall;
|
||||
spa_list_for_each_safe(call, tcall, &rfcomm->telephony_ag->call_list, link) {
|
||||
if (call->state == CALL_STATE_ACTIVE) {
|
||||
call->state = CALL_STATE_DISCONNECTED;
|
||||
hfp_hf_set_call_state(backend->log, call, CALL_STATE_DISCONNECTED);
|
||||
telephony_call_notify_updated_props(call);
|
||||
telephony_call_destroy(call);
|
||||
}
|
||||
}
|
||||
spa_list_for_each(call, &rfcomm->telephony_ag->call_list, link) {
|
||||
if (call->state == CALL_STATE_HELD) {
|
||||
call->state = CALL_STATE_ACTIVE;
|
||||
hfp_hf_set_call_state(backend->log, call, CALL_STATE_ACTIVE);
|
||||
telephony_call_notify_updated_props(call);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <deque>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
|
|
@ -54,7 +53,6 @@ namespace {
|
|||
#define MASK_BUFFERS 31
|
||||
|
||||
#define BUFFER_FLAG_OUTSTANDING (1<<0)
|
||||
#define BUFFER_FLAG_MAPPED (1<<1)
|
||||
|
||||
struct buffer {
|
||||
uint32_t id;
|
||||
|
|
@ -80,8 +78,6 @@ struct port {
|
|||
struct buffer buffers[MAX_BUFFERS];
|
||||
uint32_t n_buffers = 0;
|
||||
struct spa_list queue;
|
||||
struct spa_ringbuffer ring = SPA_RINGBUFFER_INIT();
|
||||
uint32_t ring_ids[MAX_BUFFERS];
|
||||
|
||||
static constexpr uint64_t info_all = SPA_PORT_CHANGE_MASK_FLAGS |
|
||||
SPA_PORT_CHANGE_MASK_PROPS | SPA_PORT_CHANGE_MASK_PARAMS;
|
||||
|
|
@ -155,9 +151,10 @@ struct impl {
|
|||
std::shared_ptr<Camera> camera;
|
||||
const std::unique_ptr<CameraConfiguration> config;
|
||||
|
||||
FrameBufferAllocator *allocator = nullptr;
|
||||
FrameBufferAllocator allocator;
|
||||
std::vector<std::unique_ptr<libcamera::Request>> requestPool;
|
||||
std::deque<libcamera::Request *> pendingRequests;
|
||||
spa_ringbuffer completed_requests_rb = SPA_RINGBUFFER_INIT();
|
||||
std::array<libcamera::Request *, MAX_BUFFERS> completed_requests;
|
||||
|
||||
void requestComplete(libcamera::Request *request);
|
||||
|
||||
|
|
@ -173,6 +170,38 @@ struct impl {
|
|||
std::unique_ptr<CameraConfiguration> config);
|
||||
|
||||
struct spa_dll dll;
|
||||
|
||||
void stop()
|
||||
{
|
||||
spa_loop_locked(
|
||||
data_loop,
|
||||
[](spa_loop *, bool, uint32_t, const void *, size_t, void *user_data) {
|
||||
auto *self = static_cast<impl *>(user_data);
|
||||
|
||||
if (self->source.loop)
|
||||
spa_loop_remove_source(self->data_loop, &self->source);
|
||||
|
||||
return 0;
|
||||
},
|
||||
0, nullptr, 0, this
|
||||
);
|
||||
|
||||
if (source.fd >= 0)
|
||||
spa_system_close(system, std::exchange(source.fd, -1));
|
||||
|
||||
camera->requestCompleted.disconnect(this, &impl::requestComplete);
|
||||
|
||||
if (int res = camera->stop(); res < 0) {
|
||||
spa_log_warn(log, "failed to stop camera %s: %s",
|
||||
camera->id().c_str(), spa_strerror(res));
|
||||
}
|
||||
|
||||
completed_requests_rb = SPA_RINGBUFFER_INIT();
|
||||
active = false;
|
||||
|
||||
for (auto& p : out_ports)
|
||||
spa_list_init(&p.queue);
|
||||
}
|
||||
};
|
||||
|
||||
#define CHECK_PORT(impl,direction,port_id) ((direction) == SPA_DIRECTION_OUTPUT && (port_id) == 0)
|
||||
|
|
@ -214,7 +243,7 @@ int spa_libcamera_open(struct impl *impl)
|
|||
if (int res = impl->camera->acquire(); res < 0)
|
||||
return res;
|
||||
|
||||
impl->allocator = new FrameBufferAllocator(impl->camera);
|
||||
spa_assert(!impl->allocator.allocated());
|
||||
|
||||
const ControlInfoMap &controls = impl->camera->controls();
|
||||
setup_initial_controls(controls, impl->initial_controls);
|
||||
|
|
@ -232,8 +261,8 @@ int spa_libcamera_close(struct impl *impl)
|
|||
return 0;
|
||||
|
||||
spa_log_info(impl->log, "close camera %s", impl->camera->id().c_str());
|
||||
delete impl->allocator;
|
||||
impl->allocator = nullptr;
|
||||
|
||||
spa_assert(!impl->allocator.allocated());
|
||||
|
||||
impl->camera->release();
|
||||
|
||||
|
|
@ -257,17 +286,7 @@ int spa_libcamera_buffer_recycle(struct impl *impl, struct port *port, uint32_t
|
|||
return -EINVAL;
|
||||
}
|
||||
Request *request = impl->requestPool[buffer_id].get();
|
||||
Stream *stream = port->streamConfig.stream();
|
||||
FrameBuffer *buffer = impl->allocator->buffers(stream)[buffer_id].get();
|
||||
if ((res = request->addBuffer(stream, buffer)) < 0) {
|
||||
spa_log_warn(impl->log, "can't add buffer %u for request: %s",
|
||||
buffer_id, spa_strerror(res));
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (!impl->active) {
|
||||
impl->pendingRequests.push_back(request);
|
||||
return 0;
|
||||
} else {
|
||||
if (impl->active) {
|
||||
request->controls().merge(impl->ctrls);
|
||||
impl->ctrls.clear();
|
||||
if ((res = impl->camera->queueRequest(request)) < 0) {
|
||||
|
|
@ -279,19 +298,57 @@ int spa_libcamera_buffer_recycle(struct impl *impl, struct port *port, uint32_t
|
|||
return 0;
|
||||
}
|
||||
|
||||
void freeBuffers(struct impl *impl, struct port *port)
|
||||
{
|
||||
impl->requestPool.clear();
|
||||
std::ignore = impl->allocator.free(port->streamConfig.stream());
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
std::size_t count_unique_fds(libcamera::Span<const libcamera::FrameBuffer::Plane> planes)
|
||||
{
|
||||
std::size_t c = 0;
|
||||
int fd = -1;
|
||||
|
||||
for (const auto& plane : planes) {
|
||||
const int current_fd = plane.fd.get();
|
||||
if (current_fd >= 0 && current_fd != fd) {
|
||||
c += 1;
|
||||
fd = current_fd;
|
||||
}
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
int allocBuffers(struct impl *impl, struct port *port, unsigned int count)
|
||||
{
|
||||
libcamera::Stream *stream = port->streamConfig.stream();
|
||||
int res;
|
||||
|
||||
if ((res = impl->allocator->allocate(port->streamConfig.stream())) < 0)
|
||||
if (!impl->requestPool.empty())
|
||||
return -EBUSY;
|
||||
|
||||
if ((res = impl->allocator.allocate(stream)) < 0)
|
||||
return res;
|
||||
|
||||
for (unsigned int i = 0; i < count; i++) {
|
||||
const auto& bufs = impl->allocator.buffers(stream);
|
||||
if (bufs.empty() || bufs.size() != count) {
|
||||
res = -ENOBUFS;
|
||||
goto err;
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < bufs.size(); i++) {
|
||||
std::unique_ptr<Request> request = impl->camera->createRequest(i);
|
||||
if (!request) {
|
||||
impl->requestPool.clear();
|
||||
return -ENOMEM;
|
||||
res = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
res = request->addBuffer(stream, bufs[i].get());
|
||||
if (res < 0)
|
||||
goto err;
|
||||
|
||||
impl->requestPool.push_back(std::move(request));
|
||||
}
|
||||
|
||||
|
|
@ -300,63 +357,38 @@ int allocBuffers(struct impl *impl, struct port *port, unsigned int count)
|
|||
* video frame has to be addressed using more than one memory.
|
||||
* address. Therefore, need calculate the number of discontiguous
|
||||
* memory and allocate the specified amount of memory */
|
||||
Stream *stream = impl->config->at(0).stream();
|
||||
const std::vector<std::unique_ptr<FrameBuffer>> &bufs =
|
||||
impl->allocator->buffers(stream);
|
||||
const std::vector<libcamera::FrameBuffer::Plane> &planes = bufs[0]->planes();
|
||||
int fd = -1;
|
||||
uint32_t buffers_blocks = 0;
|
||||
|
||||
for (const FrameBuffer::Plane &plane : planes) {
|
||||
const int current_fd = plane.fd.get();
|
||||
if (current_fd >= 0 && current_fd != fd) {
|
||||
buffers_blocks += 1;
|
||||
fd = current_fd;
|
||||
}
|
||||
port->buffers_blocks = count_unique_fds(bufs.front()->planes());
|
||||
if (port->buffers_blocks <= 0) {
|
||||
res = -ENOBUFS;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (buffers_blocks > 0) {
|
||||
port->buffers_blocks = buffers_blocks;
|
||||
}
|
||||
return 0;
|
||||
|
||||
err:
|
||||
freeBuffers(impl, port);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void freeBuffers(struct impl *impl, struct port *port)
|
||||
int spa_libcamera_clear_buffers(struct port *port)
|
||||
{
|
||||
impl->pendingRequests.clear();
|
||||
impl->requestPool.clear();
|
||||
impl->allocator->free(port->streamConfig.stream());
|
||||
}
|
||||
for (std::size_t i = 0; i < port->n_buffers; i++) {
|
||||
buffer *b = &port->buffers[i];
|
||||
spa_buffer *sb = b->outbuf;
|
||||
|
||||
int spa_libcamera_clear_buffers(struct impl *impl, struct port *port)
|
||||
{
|
||||
uint32_t i;
|
||||
for (std::size_t j = 0; j < sb->n_datas; j++) {
|
||||
auto *d = &sb->datas[j];
|
||||
|
||||
if (port->n_buffers == 0)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < port->n_buffers; i++) {
|
||||
struct buffer *b;
|
||||
struct spa_data *d;
|
||||
|
||||
b = &port->buffers[i];
|
||||
d = b->outbuf->datas;
|
||||
|
||||
if (SPA_FLAG_IS_SET(b->flags, BUFFER_FLAG_OUTSTANDING)) {
|
||||
spa_log_debug(impl->log, "queueing outstanding buffer %p", b);
|
||||
spa_libcamera_buffer_recycle(impl, port, i);
|
||||
}
|
||||
if (SPA_FLAG_IS_SET(b->flags, BUFFER_FLAG_MAPPED)) {
|
||||
munmap(SPA_PTROFF(b->ptr, -d[0].mapoffset, void),
|
||||
d[0].maxsize - d[0].mapoffset);
|
||||
d->type = SPA_ID_INVALID;
|
||||
d->data = nullptr;
|
||||
d->fd = -1;
|
||||
}
|
||||
|
||||
d[0].type = SPA_ID_INVALID;
|
||||
*b = {};
|
||||
}
|
||||
|
||||
freeBuffers(impl, port);
|
||||
port->n_buffers = 0;
|
||||
port->ring = SPA_RINGBUFFER_INIT();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -371,6 +403,7 @@ struct format_info {
|
|||
#define MAKE_FMT(pix,fmt,mt,mst) { pix, SPA_VIDEO_FORMAT_ ##fmt, SPA_MEDIA_TYPE_ ##mt, SPA_MEDIA_SUBTYPE_ ##mst }
|
||||
const struct format_info format_info[] = {
|
||||
/* RGB formats */
|
||||
MAKE_FMT(formats::R8, GRAY8, video, raw),
|
||||
MAKE_FMT(formats::RGB565, RGB16, video, raw),
|
||||
MAKE_FMT(formats::RGB565_BE, RGB16, video, raw),
|
||||
MAKE_FMT(formats::RGB888, BGR, video, raw),
|
||||
|
|
@ -1005,14 +1038,110 @@ int spa_libcamera_apply_controls(struct impl *impl, libcamera::ControlList&& con
|
|||
);
|
||||
}
|
||||
|
||||
void handle_completed_request(struct impl *impl, libcamera::Request *request)
|
||||
{
|
||||
const auto request_id = request->cookie();
|
||||
struct port *port = &impl->out_ports[0];
|
||||
buffer *b = &port->buffers[request_id];
|
||||
|
||||
spa_log_trace(impl->log, "%p: request %p[%" PRIu64 "] process status:%u seq:%" PRIu32,
|
||||
impl, request, request_id, static_cast<unsigned int>(request->status()),
|
||||
request->sequence());
|
||||
|
||||
if (request->status() == libcamera::Request::Status::RequestCancelled) {
|
||||
spa_log_trace(impl->log, "%p: request %p[%" PRIu64 "] cancelled",
|
||||
impl, request, request_id);
|
||||
request->reuse(libcamera::Request::ReuseFlag::ReuseBuffers);
|
||||
SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUTSTANDING);
|
||||
spa_libcamera_buffer_recycle(impl, port, b->id);
|
||||
return;
|
||||
}
|
||||
|
||||
const FrameBuffer *buffer = request->findBuffer(port->streamConfig.stream());
|
||||
if (buffer == nullptr) {
|
||||
spa_log_warn(impl->log, "%p: request %p[%" PRIu64 "] has no buffer for stream %p",
|
||||
impl, request, request_id, port->streamConfig.stream());
|
||||
return;
|
||||
}
|
||||
|
||||
const FrameMetadata &fmd = buffer->metadata();
|
||||
|
||||
if (impl->clock) {
|
||||
double target = (double)port->info.rate.num / port->info.rate.denom;
|
||||
double corr;
|
||||
|
||||
if (impl->dll.bw == 0.0) {
|
||||
spa_dll_set_bw(&impl->dll, SPA_DLL_BW_MAX, port->info.rate.denom, port->info.rate.denom);
|
||||
impl->clock->next_nsec = fmd.timestamp;
|
||||
corr = 1.0;
|
||||
} else {
|
||||
double diff = ((double)impl->clock->next_nsec - (double)fmd.timestamp) / SPA_NSEC_PER_SEC;
|
||||
double error = port->info.rate.denom * (diff - target);
|
||||
corr = spa_dll_update(&impl->dll, SPA_CLAMPD(error, -128., 128.));
|
||||
}
|
||||
/* FIXME, we should follow the driver clock and target_ values.
|
||||
* for now we ignore and use our own. */
|
||||
impl->clock->target_rate = port->rate;
|
||||
impl->clock->target_duration = 1;
|
||||
|
||||
impl->clock->nsec = fmd.timestamp;
|
||||
impl->clock->rate = port->rate;
|
||||
impl->clock->position = fmd.sequence;
|
||||
impl->clock->duration = 1;
|
||||
impl->clock->delay = 0;
|
||||
impl->clock->rate_diff = corr;
|
||||
impl->clock->next_nsec += (uint64_t) (target * SPA_NSEC_PER_SEC * corr);
|
||||
}
|
||||
|
||||
if (b->h) {
|
||||
b->h->flags = 0;
|
||||
if (fmd.status != libcamera::FrameMetadata::Status::FrameSuccess)
|
||||
b->h->flags |= SPA_META_HEADER_FLAG_CORRUPTED;
|
||||
b->h->offset = 0;
|
||||
b->h->seq = fmd.sequence;
|
||||
b->h->pts = fmd.timestamp;
|
||||
b->h->dts_offset = 0;
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < b->outbuf->n_datas; i++) {
|
||||
auto *d = &b->outbuf->datas[i];
|
||||
|
||||
d->chunk->flags = 0;
|
||||
|
||||
if (fmd.status != libcamera::FrameMetadata::Status::FrameSuccess)
|
||||
d->chunk->flags |= SPA_CHUNK_FLAG_CORRUPTED;
|
||||
}
|
||||
|
||||
request->reuse(libcamera::Request::ReuseFlag::ReuseBuffers);
|
||||
|
||||
spa_list_append(&port->queue, &b->link);
|
||||
|
||||
spa_io_buffers *io = port->io;
|
||||
if (io == nullptr) {
|
||||
b = spa_list_first(&port->queue, struct buffer, link);
|
||||
spa_list_remove(&b->link);
|
||||
SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUTSTANDING);
|
||||
spa_libcamera_buffer_recycle(impl, port, b->id);
|
||||
} else if (io->status != SPA_STATUS_HAVE_DATA) {
|
||||
if (io->buffer_id < port->n_buffers)
|
||||
spa_libcamera_buffer_recycle(impl, port, io->buffer_id);
|
||||
|
||||
b = spa_list_first(&port->queue, struct buffer, link);
|
||||
spa_list_remove(&b->link);
|
||||
SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUTSTANDING);
|
||||
|
||||
io->buffer_id = b->id;
|
||||
io->status = SPA_STATUS_HAVE_DATA;
|
||||
spa_log_trace(impl->log, "%p: now queued %" PRIu32, impl, b->id);
|
||||
}
|
||||
|
||||
spa_node_call_ready(&impl->callbacks, SPA_STATUS_HAVE_DATA);
|
||||
}
|
||||
|
||||
void libcamera_on_fd_events(struct spa_source *source)
|
||||
{
|
||||
struct impl *impl = (struct impl*) source->data;
|
||||
struct spa_io_buffers *io;
|
||||
struct port *port = &impl->out_ports[0];
|
||||
uint32_t index, buffer_id;
|
||||
struct buffer *b;
|
||||
uint32_t index;
|
||||
uint64_t cnt;
|
||||
|
||||
if (source->rmask & SPA_IO_ERR) {
|
||||
|
|
@ -1032,35 +1161,13 @@ void libcamera_on_fd_events(struct spa_source *source)
|
|||
return;
|
||||
}
|
||||
|
||||
if (spa_ringbuffer_get_read_index(&port->ring, &index) < 1) {
|
||||
spa_log_error(impl->log, "nothing is queued");
|
||||
return;
|
||||
auto avail = spa_ringbuffer_get_read_index(&impl->completed_requests_rb, &index);
|
||||
for (; avail > 0; avail--, index++) {
|
||||
auto *request = impl->completed_requests[index & MASK_BUFFERS];
|
||||
|
||||
spa_ringbuffer_read_update(&impl->completed_requests_rb, index + 1);
|
||||
handle_completed_request(impl, request);
|
||||
}
|
||||
buffer_id = port->ring_ids[index & MASK_BUFFERS];
|
||||
spa_ringbuffer_read_update(&port->ring, index + 1);
|
||||
|
||||
b = &port->buffers[buffer_id];
|
||||
spa_list_append(&port->queue, &b->link);
|
||||
|
||||
io = port->io;
|
||||
if (io == nullptr) {
|
||||
b = spa_list_first(&port->queue, struct buffer, link);
|
||||
spa_list_remove(&b->link);
|
||||
SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUTSTANDING);
|
||||
spa_libcamera_buffer_recycle(impl, port, b->id);
|
||||
} else if (io->status != SPA_STATUS_HAVE_DATA) {
|
||||
if (io->buffer_id < port->n_buffers)
|
||||
spa_libcamera_buffer_recycle(impl, port, io->buffer_id);
|
||||
|
||||
b = spa_list_first(&port->queue, struct buffer, link);
|
||||
spa_list_remove(&b->link);
|
||||
SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUTSTANDING);
|
||||
|
||||
io->buffer_id = b->id;
|
||||
io->status = SPA_STATUS_HAVE_DATA;
|
||||
spa_log_trace(impl->log, "libcamera %p: now queued %d", impl, b->id);
|
||||
}
|
||||
spa_node_call_ready(&impl->callbacks, SPA_STATUS_HAVE_DATA);
|
||||
}
|
||||
|
||||
int spa_libcamera_use_buffers(struct impl *impl, struct port *port,
|
||||
|
|
@ -1102,7 +1209,7 @@ spa_libcamera_alloc_buffers(struct impl *impl, struct port *port,
|
|||
|
||||
Stream *stream = impl->config->at(0).stream();
|
||||
const std::vector<std::unique_ptr<FrameBuffer>> &bufs =
|
||||
impl->allocator->buffers(stream);
|
||||
impl->allocator.buffers(stream);
|
||||
|
||||
if (n_buffers > 0) {
|
||||
if (bufs.size() != n_buffers)
|
||||
|
|
@ -1112,10 +1219,8 @@ spa_libcamera_alloc_buffers(struct impl *impl, struct port *port,
|
|||
|
||||
if (d[0].type != SPA_ID_INVALID && d[0].type & (1u << SPA_DATA_DmaBuf)) {
|
||||
port->memtype = SPA_DATA_DmaBuf;
|
||||
} else if (d[0].type != SPA_ID_INVALID && d[0].type & (1u << SPA_DATA_MemFd)) {
|
||||
} else if (d[0].type & (1u << SPA_DATA_MemFd)) {
|
||||
port->memtype = SPA_DATA_MemFd;
|
||||
} else if (d[0].type & (1u << SPA_DATA_MemPtr)) {
|
||||
port->memtype = SPA_DATA_MemPtr;
|
||||
} else {
|
||||
spa_log_error(impl->log, "can't use buffers of type %d", d[0].type);
|
||||
return -EINVAL;
|
||||
|
|
@ -1133,7 +1238,7 @@ spa_libcamera_alloc_buffers(struct impl *impl, struct port *port,
|
|||
b = &port->buffers[i];
|
||||
b->id = i;
|
||||
b->outbuf = buffers[i];
|
||||
b->flags = BUFFER_FLAG_OUTSTANDING;
|
||||
b->flags = 0;
|
||||
b->h = (struct spa_meta_header*)spa_buffer_find_meta_data(buffers[i], SPA_META_Header, sizeof(*b->h));
|
||||
|
||||
b->videotransform = (struct spa_meta_videotransform*)spa_buffer_find_meta_data(
|
||||
|
|
@ -1186,28 +1291,11 @@ spa_libcamera_alloc_buffers(struct impl *impl, struct port *port,
|
|||
d[j].fd = bufs[i]->planes()[j].fd.get();
|
||||
spa_log_debug(impl->log, "Got fd = %" PRId64 " for buffer: #%d", d[j].fd, i);
|
||||
d[j].data = nullptr;
|
||||
}
|
||||
else if (port->memtype == SPA_DATA_MemPtr) {
|
||||
d[j].fd = -1;
|
||||
d[j].data = mmap(nullptr,
|
||||
d[j].maxsize + d[j].mapoffset,
|
||||
PROT_READ, MAP_SHARED,
|
||||
bufs[i]->planes()[j].fd.get(),
|
||||
0);
|
||||
if (d[j].data == MAP_FAILED) {
|
||||
spa_log_error(impl->log, "mmap: %m");
|
||||
continue;
|
||||
}
|
||||
b->ptr = d[j].data;
|
||||
SPA_FLAG_SET(b->flags, BUFFER_FLAG_MAPPED);
|
||||
spa_log_debug(impl->log, "mmap ptr:%p", d[j].data);
|
||||
} else {
|
||||
spa_log_error(impl->log, "invalid buffer type");
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
spa_libcamera_buffer_recycle(impl, port, i);
|
||||
}
|
||||
|
||||
port->n_buffers = n_buffers;
|
||||
|
|
@ -1220,89 +1308,22 @@ spa_libcamera_alloc_buffers(struct impl *impl, struct port *port,
|
|||
void impl::requestComplete(libcamera::Request *request)
|
||||
{
|
||||
struct impl *impl = this;
|
||||
struct port *port = &impl->out_ports[0];
|
||||
Stream *stream = port->streamConfig.stream();
|
||||
uint32_t index, buffer_id;
|
||||
struct buffer *b;
|
||||
uint32_t index;
|
||||
|
||||
spa_log_debug(impl->log, "request complete");
|
||||
spa_log_trace(impl->log, "%p: request %p[%" PRIu64 "] completed status:%u seq:%" PRIu32,
|
||||
impl, request, request->cookie(),
|
||||
static_cast<unsigned int>(request->status()),
|
||||
request->sequence());
|
||||
|
||||
buffer_id = request->cookie();
|
||||
b = &port->buffers[buffer_id];
|
||||
|
||||
if ((request->status() == Request::RequestCancelled)) {
|
||||
spa_log_debug(impl->log, "Request was cancelled");
|
||||
request->reuse();
|
||||
SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUTSTANDING);
|
||||
spa_libcamera_buffer_recycle(impl, port, b->id);
|
||||
return;
|
||||
}
|
||||
FrameBuffer *buffer = request->findBuffer(stream);
|
||||
if (buffer == nullptr) {
|
||||
spa_log_warn(impl->log, "unknown buffer");
|
||||
return;
|
||||
}
|
||||
const FrameMetadata &fmd = buffer->metadata();
|
||||
|
||||
if (impl->clock) {
|
||||
double target = (double)port->info.rate.num / port->info.rate.denom;
|
||||
double corr;
|
||||
|
||||
if (impl->dll.bw == 0.0) {
|
||||
spa_dll_set_bw(&impl->dll, SPA_DLL_BW_MAX, port->info.rate.denom, port->info.rate.denom);
|
||||
impl->clock->next_nsec = fmd.timestamp;
|
||||
corr = 1.0;
|
||||
} else {
|
||||
double diff = ((double)impl->clock->next_nsec - (double)fmd.timestamp) / SPA_NSEC_PER_SEC;
|
||||
double error = port->info.rate.denom * (diff - target);
|
||||
corr = spa_dll_update(&impl->dll, SPA_CLAMPD(error, -128., 128.));
|
||||
}
|
||||
/* FIXME, we should follow the driver clock and target_ values.
|
||||
* for now we ignore and use our own. */
|
||||
impl->clock->target_rate = port->rate;
|
||||
impl->clock->target_duration = 1;
|
||||
|
||||
impl->clock->nsec = fmd.timestamp;
|
||||
impl->clock->rate = port->rate;
|
||||
impl->clock->position = fmd.sequence;
|
||||
impl->clock->duration = 1;
|
||||
impl->clock->delay = 0;
|
||||
impl->clock->rate_diff = corr;
|
||||
impl->clock->next_nsec += (uint64_t) (target * SPA_NSEC_PER_SEC * corr);
|
||||
}
|
||||
if (b->h) {
|
||||
b->h->flags = 0;
|
||||
if (fmd.status != FrameMetadata::Status::FrameSuccess)
|
||||
b->h->flags |= SPA_META_HEADER_FLAG_CORRUPTED;
|
||||
b->h->offset = 0;
|
||||
b->h->seq = fmd.sequence;
|
||||
b->h->pts = fmd.timestamp;
|
||||
b->h->dts_offset = 0;
|
||||
}
|
||||
request->reuse();
|
||||
|
||||
spa_ringbuffer_get_write_index(&port->ring, &index);
|
||||
port->ring_ids[index & MASK_BUFFERS] = buffer_id;
|
||||
spa_ringbuffer_write_update(&port->ring, index + 1);
|
||||
spa_ringbuffer_get_write_index(&impl->completed_requests_rb, &index);
|
||||
impl->completed_requests[index & MASK_BUFFERS] = request;
|
||||
spa_ringbuffer_write_update(&impl->completed_requests_rb, index + 1);
|
||||
|
||||
if (spa_system_eventfd_write(impl->system, impl->source.fd, 1) < 0)
|
||||
spa_log_error(impl->log, "Failed to write on event fd");
|
||||
|
||||
}
|
||||
|
||||
int do_remove_source(struct spa_loop *loop,
|
||||
bool async,
|
||||
uint32_t seq,
|
||||
const void *data,
|
||||
size_t size,
|
||||
void *user_data)
|
||||
{
|
||||
auto *impl = static_cast<struct impl *>(user_data);
|
||||
if (impl->source.loop)
|
||||
spa_loop_remove_source(loop, &impl->source);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int spa_libcamera_stream_on(struct impl *impl)
|
||||
{
|
||||
struct port *port = &impl->out_ports[0];
|
||||
|
|
@ -1316,76 +1337,60 @@ int spa_libcamera_stream_on(struct impl *impl)
|
|||
if (impl->active)
|
||||
return 0;
|
||||
|
||||
spa_log_info(impl->log, "starting camera %s", impl->camera->id().c_str());
|
||||
if ((res = impl->camera->start(&impl->initial_controls)) < 0)
|
||||
return res == -EACCES ? -EBUSY : res;
|
||||
|
||||
impl->camera->requestCompleted.connect(impl, &impl::requestComplete);
|
||||
|
||||
res = spa_system_eventfd_create(impl->system, SPA_FD_CLOEXEC | SPA_FD_NONBLOCK);
|
||||
if (res < 0)
|
||||
return res;
|
||||
goto err_stop;
|
||||
|
||||
impl->source.fd = res;
|
||||
impl->source.func = libcamera_on_fd_events;
|
||||
impl->source.data = impl;
|
||||
impl->source.mask = SPA_IO_IN | SPA_IO_ERR;
|
||||
impl->source.rmask = 0;
|
||||
res = spa_loop_add_source(impl->data_loop, &impl->source);
|
||||
if (res < 0)
|
||||
goto err_close_source;
|
||||
|
||||
spa_log_info(impl->log, "starting camera %s", impl->camera->id().c_str());
|
||||
if ((res = impl->camera->start(&impl->initial_controls)) < 0)
|
||||
goto err_remove_source;
|
||||
for (auto& req : impl->requestPool) {
|
||||
req->reuse(libcamera::Request::ReuseFlag::ReuseBuffers);
|
||||
|
||||
impl->camera->requestCompleted.connect(impl, &impl::requestComplete);
|
||||
|
||||
for (Request *req : impl->pendingRequests) {
|
||||
if ((res = impl->camera->queueRequest(req)) < 0)
|
||||
goto err_stop_camera;
|
||||
if ((res = impl->camera->queueRequest(req.get())) < 0)
|
||||
goto err_stop;
|
||||
}
|
||||
impl->pendingRequests.clear();
|
||||
|
||||
impl->dll.bw = 0.0;
|
||||
impl->active = true;
|
||||
|
||||
res = spa_loop_locked(
|
||||
impl->data_loop,
|
||||
[](spa_loop *, bool, uint32_t, const void *, size_t, void *user_data)
|
||||
{
|
||||
auto *impl = static_cast<struct impl *>(user_data);
|
||||
return spa_loop_add_source(impl->data_loop, &impl->source);
|
||||
},
|
||||
0, nullptr, 0, impl
|
||||
);
|
||||
if (res < 0)
|
||||
goto err_stop;
|
||||
|
||||
return 0;
|
||||
|
||||
err_stop_camera:
|
||||
impl->camera->stop();
|
||||
impl->camera->requestCompleted.disconnect(impl, &impl::requestComplete);
|
||||
err_remove_source:
|
||||
spa_loop_locked(impl->data_loop, do_remove_source, 0, nullptr, 0, impl);
|
||||
err_close_source:
|
||||
spa_system_close(impl->system, std::exchange(impl->source.fd, -1));
|
||||
err_stop:
|
||||
impl->stop();
|
||||
|
||||
return res == -EACCES ? -EBUSY : res;
|
||||
return res;
|
||||
}
|
||||
|
||||
int spa_libcamera_stream_off(struct impl *impl)
|
||||
{
|
||||
struct port *port = &impl->out_ports[0];
|
||||
int res;
|
||||
|
||||
if (!impl->active) {
|
||||
for (std::unique_ptr<Request> &req : impl->requestPool)
|
||||
req->reuse();
|
||||
if (!impl->active)
|
||||
return 0;
|
||||
}
|
||||
|
||||
impl->active = false;
|
||||
spa_log_info(impl->log, "stopping camera %s", impl->camera->id().c_str());
|
||||
impl->pendingRequests.clear();
|
||||
|
||||
if ((res = impl->camera->stop()) < 0) {
|
||||
spa_log_warn(impl->log, "error stopping camera %s: %s",
|
||||
impl->camera->id().c_str(), spa_strerror(res));
|
||||
}
|
||||
|
||||
impl->camera->requestCompleted.disconnect(impl, &impl::requestComplete);
|
||||
|
||||
spa_loop_locked(impl->data_loop, do_remove_source, 0, nullptr, 0, impl);
|
||||
if (impl->source.fd >= 0) {
|
||||
spa_system_close(impl->system, impl->source.fd);
|
||||
impl->source.fd = -1;
|
||||
}
|
||||
|
||||
spa_list_init(&port->queue);
|
||||
impl->stop();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1824,21 +1829,24 @@ next:
|
|||
int port_set_format(struct impl *impl, struct port *port,
|
||||
uint32_t flags, const struct spa_pod *format)
|
||||
{
|
||||
struct spa_video_info info;
|
||||
int res;
|
||||
const bool try_only = SPA_FLAG_IS_SET(flags, SPA_NODE_PARAM_FLAG_TEST_ONLY);
|
||||
|
||||
if (!try_only) {
|
||||
spa_libcamera_stream_off(impl);
|
||||
spa_libcamera_clear_buffers(port);
|
||||
freeBuffers(impl, port);
|
||||
port->current_format.reset();
|
||||
}
|
||||
|
||||
if (format == nullptr) {
|
||||
if (!port->current_format)
|
||||
return 0;
|
||||
|
||||
spa_libcamera_stream_off(impl);
|
||||
spa_libcamera_clear_buffers(impl, port);
|
||||
port->current_format.reset();
|
||||
|
||||
if (!try_only)
|
||||
spa_libcamera_close(impl);
|
||||
goto done;
|
||||
} else {
|
||||
spa_video_info info;
|
||||
int res;
|
||||
|
||||
spa_zero(info);
|
||||
|
||||
if ((res = spa_format_parse(format, &info.media_type, &info.media_subtype)) < 0)
|
||||
return res;
|
||||
|
||||
|
|
@ -1854,54 +1862,32 @@ int port_set_format(struct impl *impl, struct port *port,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (port->current_format && info.media_type == port->current_format->media_type &&
|
||||
info.media_subtype == port->current_format->media_subtype &&
|
||||
info.info.raw.format == port->current_format->info.raw.format &&
|
||||
info.info.raw.size.width == port->current_format->info.raw.size.width &&
|
||||
info.info.raw.size.height == port->current_format->info.raw.size.height &&
|
||||
info.info.raw.flags == port->current_format->info.raw.flags &&
|
||||
(!(info.info.raw.flags & SPA_VIDEO_FLAG_MODIFIER) ||
|
||||
(info.info.raw.modifier == port->current_format->info.raw.modifier)))
|
||||
return 0;
|
||||
break;
|
||||
case SPA_MEDIA_SUBTYPE_mjpg:
|
||||
if (spa_format_video_mjpg_parse(format, &info.info.mjpg) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (port->current_format && info.media_type == port->current_format->media_type &&
|
||||
info.media_subtype == port->current_format->media_subtype &&
|
||||
info.info.mjpg.size.width == port->current_format->info.mjpg.size.width &&
|
||||
info.info.mjpg.size.height == port->current_format->info.mjpg.size.height)
|
||||
return 0;
|
||||
break;
|
||||
case SPA_MEDIA_SUBTYPE_h264:
|
||||
if (spa_format_video_h264_parse(format, &info.info.h264) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (port->current_format && info.media_type == port->current_format->media_type &&
|
||||
info.media_subtype == port->current_format->media_subtype &&
|
||||
info.info.h264.size.width == port->current_format->info.h264.size.width &&
|
||||
info.info.h264.size.height == port->current_format->info.h264.size.height)
|
||||
return 0;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (port->current_format && !(flags & SPA_NODE_PARAM_FLAG_TEST_ONLY)) {
|
||||
spa_libcamera_use_buffers(impl, port, nullptr, 0);
|
||||
port->current_format.reset();
|
||||
}
|
||||
res = spa_libcamera_set_format(impl, port, &info, try_only);
|
||||
if (res < 0)
|
||||
return res;
|
||||
|
||||
if (spa_libcamera_set_format(impl, port, &info, flags & SPA_NODE_PARAM_FLAG_TEST_ONLY) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (!(flags & SPA_NODE_PARAM_FLAG_TEST_ONLY)) {
|
||||
if (!try_only)
|
||||
port->current_format = info;
|
||||
}
|
||||
|
||||
done:
|
||||
if (try_only)
|
||||
return 0;
|
||||
|
||||
impl->info.change_mask |= SPA_NODE_CHANGE_MASK_PARAMS;
|
||||
port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
|
||||
if (port->current_format) {
|
||||
|
|
@ -1961,7 +1947,7 @@ int impl_node_port_use_buffers(void *object,
|
|||
|
||||
if (port->n_buffers) {
|
||||
spa_libcamera_stream_off(impl);
|
||||
if ((res = spa_libcamera_clear_buffers(impl, port)) < 0)
|
||||
if ((res = spa_libcamera_clear_buffers(port)) < 0)
|
||||
return res;
|
||||
}
|
||||
if (n_buffers > 0 && !port->current_format)
|
||||
|
|
@ -2167,7 +2153,8 @@ impl::impl(spa_log *log, spa_loop *data_loop, spa_system *system,
|
|||
out_ports{{this}},
|
||||
manager(std::move(manager)),
|
||||
camera(std::move(camera)),
|
||||
config(std::move(config))
|
||||
config(std::move(config)),
|
||||
allocator(this->camera)
|
||||
{
|
||||
libcamera_log_topic_init(log);
|
||||
|
||||
|
|
|
|||
|
|
@ -1206,7 +1206,7 @@ gst_pipewire_src_negotiate (GstBaseSrc * basesrc)
|
|||
|
||||
gst_pipewire_clock_reset (GST_PIPEWIRE_CLOCK (pwsrc->stream->clock), 0);
|
||||
|
||||
GST_DEBUG_OBJECT (pwsrc, "set format %" GST_PTR_FORMAT, negotiated_caps);
|
||||
GST_INFO_OBJECT (pwsrc, "set format %" GST_PTR_FORMAT, negotiated_caps);
|
||||
result = gst_base_src_set_caps (GST_BASE_SRC (pwsrc), negotiated_caps);
|
||||
if (!result)
|
||||
goto no_caps;
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@
|
|||
* ## Module Options
|
||||
*
|
||||
* - `jack.library`: the libjack to load, by default libjack.so.0 is searched in
|
||||
* JACK_PATH directories and then some standard library paths.
|
||||
* LIBJACK_PATH directories and then some standard library paths.
|
||||
* Can be an absolute path.
|
||||
* - `jack.server`: the name of the JACK server to tunnel to.
|
||||
* - `jack.client-name`: the name of the JACK client.
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ pwtest_deps = [
|
|||
pipewire_dep,
|
||||
mathlib,
|
||||
dl_lib,
|
||||
cap_lib,
|
||||
epoll_shim_dep
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -19,9 +19,6 @@
|
|||
#ifdef HAVE_PIDFD_OPEN
|
||||
#include <sys/syscall.h>
|
||||
#endif
|
||||
#ifdef HAVE_LIBCAP
|
||||
#include <sys/capability.h>
|
||||
#endif
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/resource.h>
|
||||
|
|
@ -33,6 +30,7 @@
|
|||
#include <valgrind/valgrind.h>
|
||||
|
||||
#include "spa/utils/ansi.h"
|
||||
#include "spa/utils/cleanup.h"
|
||||
#include "spa/utils/string.h"
|
||||
#include "spa/utils/defs.h"
|
||||
#include "spa/utils/list.h"
|
||||
|
|
@ -1298,39 +1296,20 @@ static void list_tests(struct pwtest_context *ctx)
|
|||
|
||||
static bool is_debugger_attached(void)
|
||||
{
|
||||
bool rc = false;
|
||||
#ifdef HAVE_LIBCAP
|
||||
int status;
|
||||
int pid = fork();
|
||||
spa_autofree char *line = NULL;
|
||||
size_t length = 0;
|
||||
|
||||
if (pid == -1)
|
||||
return 0;
|
||||
spa_autoptr(FILE) f = fopen("/proc/self/status", "re");
|
||||
if (!f)
|
||||
return false;
|
||||
|
||||
if (pid == 0) {
|
||||
int ppid = getppid();
|
||||
cap_t caps = cap_get_pid(ppid);
|
||||
cap_flag_value_t cap_val;
|
||||
|
||||
if (cap_get_flag(caps, CAP_SYS_PTRACE, CAP_EFFECTIVE, &cap_val) == -1 ||
|
||||
cap_val != CAP_SET)
|
||||
_exit(false);
|
||||
|
||||
if (ptrace(PTRACE_ATTACH, ppid, NULL, 0) == 0) {
|
||||
waitpid(ppid, NULL, 0);
|
||||
ptrace(PTRACE_CONT, ppid, NULL, 0);
|
||||
ptrace(PTRACE_DETACH, ppid, NULL, 0);
|
||||
rc = false;
|
||||
} else {
|
||||
rc = true;
|
||||
}
|
||||
_exit(rc);
|
||||
} else {
|
||||
waitpid(pid, &status, 0);
|
||||
rc = WEXITSTATUS(status);
|
||||
while (getline(&line, &length, f) >= 0) {
|
||||
unsigned int tracer_pid;
|
||||
if (sscanf(line, "TracerPid: %u", &tracer_pid) == 1)
|
||||
return tracer_pid > 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
return !!rc;
|
||||
return false;
|
||||
}
|
||||
|
||||
static void usage(FILE *fp, const char *progname)
|
||||
|
|
|
|||
|
|
@ -853,9 +853,6 @@ PWTEST(utils_snprintf_abort_neg_size)
|
|||
size_t size = pwtest_get_iteration(current_test);
|
||||
char dest[8];
|
||||
|
||||
if (RUNNING_ON_VALGRIND)
|
||||
return PWTEST_SKIP;
|
||||
|
||||
spa_scnprintf(dest, size, "1234"); /* expected to abort() */
|
||||
|
||||
return PWTEST_FAIL;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue