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'
|
- project: 'freedesktop/ci-templates'
|
||||||
ref: *templates_sha
|
ref: *templates_sha
|
||||||
file: '/templates/alpine.yml'
|
file: '/templates/alpine.yml'
|
||||||
|
- project: 'freedesktop/ci-templates'
|
||||||
|
ref: *templates_sha
|
||||||
|
file: '/templates/debian.yml'
|
||||||
|
|
||||||
.fedora:
|
.fedora:
|
||||||
variables:
|
variables:
|
||||||
|
|
@ -102,36 +105,6 @@ include:
|
||||||
# FDO_DISTRIBUTION_EXEC: >-
|
# FDO_DISTRIBUTION_EXEC: >-
|
||||||
# pip3 install meson
|
# 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:
|
.ubuntu:
|
||||||
variables:
|
variables:
|
||||||
# Update this tag when you want to trigger a rebuild
|
# Update this tag when you want to trigger a rebuild
|
||||||
|
|
@ -171,6 +144,30 @@ include:
|
||||||
# FDO_DISTRIBUTION_EXEC: >-
|
# FDO_DISTRIBUTION_EXEC: >-
|
||||||
# pip3 install meson
|
# 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:
|
.alpine:
|
||||||
variables:
|
variables:
|
||||||
# Update this tag when you want to trigger a rebuild
|
# Update this tag when you want to trigger a rebuild
|
||||||
|
|
@ -243,14 +240,8 @@ include:
|
||||||
- echo "Building with meson options $MESON_OPTIONS"
|
- echo "Building with meson options $MESON_OPTIONS"
|
||||||
- meson setup "$BUILD_DIR" --prefix="$PREFIX" $MESON_OPTIONS
|
- meson setup "$BUILD_DIR" --prefix="$PREFIX" $MESON_OPTIONS
|
||||||
- meson compile -C "$BUILD_DIR" $COMPILE_ARGS
|
- meson compile -C "$BUILD_DIR" $COMPILE_ARGS
|
||||||
- |
|
- meson test -C "$BUILD_DIR" --no-rebuild
|
||||||
if [ -z "$MESON_SKIP_TEST" ]; then
|
- meson install -C "$BUILD_DIR" --no-rebuild
|
||||||
meson test -C "$BUILD_DIR" --no-rebuild
|
|
||||||
fi
|
|
||||||
- |
|
|
||||||
if [ -z "$MESON_SKIP_INSTALL" ]; then
|
|
||||||
meson install -C "$BUILD_DIR" --no-rebuild
|
|
||||||
fi
|
|
||||||
artifacts:
|
artifacts:
|
||||||
name: pipewire-$CI_COMMIT_SHA
|
name: pipewire-$CI_COMMIT_SHA
|
||||||
when: always
|
when: always
|
||||||
|
|
@ -265,17 +256,21 @@ container_ubuntu:
|
||||||
variables:
|
variables:
|
||||||
GIT_STRATEGY: none # no need to pull the whole tree for rebuilding the image
|
GIT_STRATEGY: none # no need to pull the whole tree for rebuilding the image
|
||||||
|
|
||||||
container_fedora:
|
container_debian:
|
||||||
extends:
|
extends:
|
||||||
- .fedora
|
- .debian
|
||||||
- .fdo.container-build@fedora
|
- .debian-archictectures
|
||||||
|
- .fdo.container-build@debian
|
||||||
stage: container
|
stage: container
|
||||||
variables:
|
variables:
|
||||||
GIT_STRATEGY: none # no need to pull the whole tree for rebuilding the image
|
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:
|
extends:
|
||||||
- .fedora_x86
|
- .fedora
|
||||||
- .fdo.container-build@fedora
|
- .fdo.container-build@fedora
|
||||||
stage: container
|
stage: container
|
||||||
variables:
|
variables:
|
||||||
|
|
@ -311,6 +306,44 @@ build_on_ubuntu:
|
||||||
variables:
|
variables:
|
||||||
MESON_OPTIONS: "-Dsession-managers=[] -Dsnap=enabled"
|
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:
|
.build_on_fedora:
|
||||||
extends:
|
extends:
|
||||||
- .fedora
|
- .fedora
|
||||||
|
|
@ -406,21 +439,6 @@ build_on_fedora_html_docs:
|
||||||
rules:
|
rules:
|
||||||
- !reference [pages, 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:
|
build_on_alpine:
|
||||||
extends:
|
extends:
|
||||||
- .alpine
|
- .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')
|
summary({'libusb (Bluetooth quirks)': libusb_dep.found()}, bool_yn: true, section: 'Backend')
|
||||||
cdata.set('HAVE_LIBUSB', libusb_dep.found())
|
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'))
|
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')
|
summary({'GLib-2.0 (Flatpak support)': glib2_dep.found()}, bool_yn: true, section: 'Misc dependencies')
|
||||||
flatpak_support = glib2_dep.found()
|
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)
|
static void rfcomm_free(struct rfcomm *rfcomm)
|
||||||
{
|
{
|
||||||
struct updated_call *updated_call;
|
struct updated_call *updated_call;
|
||||||
|
struct rfcomm_cmd *cmd;
|
||||||
|
|
||||||
spa_list_consume(updated_call, &rfcomm->updated_call_list, link) {
|
spa_list_consume(updated_call, &rfcomm->updated_call_list, link) {
|
||||||
spa_list_remove(&updated_call->link);
|
spa_list_remove(&updated_call->link);
|
||||||
free(updated_call);
|
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);
|
codec_switch_stop_timer(rfcomm);
|
||||||
if (rfcomm->telephony_ag) {
|
if (rfcomm->telephony_ag) {
|
||||||
telephony_ag_destroy(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,
|
.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)
|
static void hfp_hf_remove_disconnected_calls(struct rfcomm *rfcomm)
|
||||||
{
|
{
|
||||||
struct impl *backend = rfcomm->backend;
|
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");
|
spa_log_debug(backend->log, "call %d -> %s", call->id, found ? "updated" : "disconnected");
|
||||||
|
|
||||||
if (!found) {
|
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_notify_updated_props(call);
|
||||||
telephony_call_destroy(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) {
|
spa_list_for_each_safe(call, tcall, &rfcomm->telephony_ag->call_list, link) {
|
||||||
if (call->state == CALL_STATE_DIALING || call->state == CALL_STATE_ALERTING ||
|
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_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_notify_updated_props(call);
|
||||||
telephony_call_destroy(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;
|
struct spa_bt_telephony_call *call;
|
||||||
spa_list_for_each(call, &rfcomm->telephony_ag->call_list, link) {
|
spa_list_for_each(call, &rfcomm->telephony_ag->call_list, link) {
|
||||||
if (call->state == CALL_STATE_DIALING) {
|
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);
|
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;
|
struct spa_bt_telephony_call *call, *tcall;
|
||||||
spa_list_for_each_safe(call, tcall, &rfcomm->telephony_ag->call_list, link) {
|
spa_list_for_each_safe(call, tcall, &rfcomm->telephony_ag->call_list, link) {
|
||||||
if (call->state == CALL_STATE_ACTIVE) {
|
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_notify_updated_props(call);
|
||||||
telephony_call_destroy(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) {
|
spa_list_for_each(call, &rfcomm->telephony_ag->call_list, link) {
|
||||||
if (call->state == CALL_STATE_DIALING || call->state == CALL_STATE_ALERTING ||
|
if (call->state == CALL_STATE_DIALING || call->state == CALL_STATE_ALERTING ||
|
||||||
call->state == CALL_STATE_INCOMING) {
|
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);
|
telephony_call_notify_updated_props(call);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2135,7 +2152,7 @@ static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token)
|
||||||
bool found_waiting = false;
|
bool found_waiting = false;
|
||||||
spa_list_for_each_safe(call, tcall, &rfcomm->telephony_ag->call_list, link) {
|
spa_list_for_each_safe(call, tcall, &rfcomm->telephony_ag->call_list, link) {
|
||||||
if (call->state == CALL_STATE_WAITING) {
|
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_notify_updated_props(call);
|
||||||
telephony_call_destroy(call);
|
telephony_call_destroy(call);
|
||||||
found_waiting = true;
|
found_waiting = true;
|
||||||
|
|
@ -2145,7 +2162,7 @@ static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token)
|
||||||
if (!found_waiting) {
|
if (!found_waiting) {
|
||||||
spa_list_for_each_safe(call, tcall, &rfcomm->telephony_ag->call_list, link) {
|
spa_list_for_each_safe(call, tcall, &rfcomm->telephony_ag->call_list, link) {
|
||||||
if (call->state == CALL_STATE_HELD) {
|
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_notify_updated_props(call);
|
||||||
telephony_call_destroy(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) {
|
spa_list_for_each(call, &rfcomm->telephony_ag->call_list, link) {
|
||||||
bool changed = false;
|
bool changed = false;
|
||||||
if (call->state == CALL_STATE_ACTIVE) {
|
if (call->state == CALL_STATE_ACTIVE) {
|
||||||
call->state = CALL_STATE_HELD;
|
hfp_hf_set_call_state(backend->log, call, CALL_STATE_HELD);
|
||||||
changed = true;
|
changed = true;
|
||||||
} else if (call->state == CALL_STATE_HELD) {
|
} 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;
|
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) {
|
spa_list_for_each(call, &rfcomm->telephony_ag->call_list, link) {
|
||||||
bool changed = false;
|
bool changed = false;
|
||||||
if (call->state == CALL_STATE_ACTIVE || call->state == CALL_STATE_WAITING) {
|
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;
|
changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2420,14 +2437,14 @@ static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token)
|
||||||
struct spa_bt_telephony_call *call, *tcall;
|
struct spa_bt_telephony_call *call, *tcall;
|
||||||
spa_list_for_each_safe(call, tcall, &rfcomm->telephony_ag->call_list, link) {
|
spa_list_for_each_safe(call, tcall, &rfcomm->telephony_ag->call_list, link) {
|
||||||
if (call->state == CALL_STATE_ACTIVE) {
|
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_notify_updated_props(call);
|
||||||
telephony_call_destroy(call);
|
telephony_call_destroy(call);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
spa_list_for_each(call, &rfcomm->telephony_ag->call_list, link) {
|
spa_list_for_each(call, &rfcomm->telephony_ag->call_list, link) {
|
||||||
if (call->state == CALL_STATE_HELD) {
|
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);
|
telephony_call_notify_updated_props(call);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <deque>
|
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
@ -54,7 +53,6 @@ namespace {
|
||||||
#define MASK_BUFFERS 31
|
#define MASK_BUFFERS 31
|
||||||
|
|
||||||
#define BUFFER_FLAG_OUTSTANDING (1<<0)
|
#define BUFFER_FLAG_OUTSTANDING (1<<0)
|
||||||
#define BUFFER_FLAG_MAPPED (1<<1)
|
|
||||||
|
|
||||||
struct buffer {
|
struct buffer {
|
||||||
uint32_t id;
|
uint32_t id;
|
||||||
|
|
@ -80,8 +78,6 @@ struct port {
|
||||||
struct buffer buffers[MAX_BUFFERS];
|
struct buffer buffers[MAX_BUFFERS];
|
||||||
uint32_t n_buffers = 0;
|
uint32_t n_buffers = 0;
|
||||||
struct spa_list queue;
|
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 |
|
static constexpr uint64_t info_all = SPA_PORT_CHANGE_MASK_FLAGS |
|
||||||
SPA_PORT_CHANGE_MASK_PROPS | SPA_PORT_CHANGE_MASK_PARAMS;
|
SPA_PORT_CHANGE_MASK_PROPS | SPA_PORT_CHANGE_MASK_PARAMS;
|
||||||
|
|
@ -155,9 +151,10 @@ struct impl {
|
||||||
std::shared_ptr<Camera> camera;
|
std::shared_ptr<Camera> camera;
|
||||||
const std::unique_ptr<CameraConfiguration> config;
|
const std::unique_ptr<CameraConfiguration> config;
|
||||||
|
|
||||||
FrameBufferAllocator *allocator = nullptr;
|
FrameBufferAllocator allocator;
|
||||||
std::vector<std::unique_ptr<libcamera::Request>> requestPool;
|
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);
|
void requestComplete(libcamera::Request *request);
|
||||||
|
|
||||||
|
|
@ -173,6 +170,38 @@ struct impl {
|
||||||
std::unique_ptr<CameraConfiguration> config);
|
std::unique_ptr<CameraConfiguration> config);
|
||||||
|
|
||||||
struct spa_dll dll;
|
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)
|
#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)
|
if (int res = impl->camera->acquire(); res < 0)
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
impl->allocator = new FrameBufferAllocator(impl->camera);
|
spa_assert(!impl->allocator.allocated());
|
||||||
|
|
||||||
const ControlInfoMap &controls = impl->camera->controls();
|
const ControlInfoMap &controls = impl->camera->controls();
|
||||||
setup_initial_controls(controls, impl->initial_controls);
|
setup_initial_controls(controls, impl->initial_controls);
|
||||||
|
|
@ -232,8 +261,8 @@ int spa_libcamera_close(struct impl *impl)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
spa_log_info(impl->log, "close camera %s", impl->camera->id().c_str());
|
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();
|
impl->camera->release();
|
||||||
|
|
||||||
|
|
@ -257,17 +286,7 @@ int spa_libcamera_buffer_recycle(struct impl *impl, struct port *port, uint32_t
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
Request *request = impl->requestPool[buffer_id].get();
|
Request *request = impl->requestPool[buffer_id].get();
|
||||||
Stream *stream = port->streamConfig.stream();
|
if (impl->active) {
|
||||||
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 {
|
|
||||||
request->controls().merge(impl->ctrls);
|
request->controls().merge(impl->ctrls);
|
||||||
impl->ctrls.clear();
|
impl->ctrls.clear();
|
||||||
if ((res = impl->camera->queueRequest(request)) < 0) {
|
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;
|
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)
|
int allocBuffers(struct impl *impl, struct port *port, unsigned int count)
|
||||||
{
|
{
|
||||||
|
libcamera::Stream *stream = port->streamConfig.stream();
|
||||||
int res;
|
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;
|
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);
|
std::unique_ptr<Request> request = impl->camera->createRequest(i);
|
||||||
if (!request) {
|
if (!request) {
|
||||||
impl->requestPool.clear();
|
res = -ENOMEM;
|
||||||
return -ENOMEM;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
res = request->addBuffer(stream, bufs[i].get());
|
||||||
|
if (res < 0)
|
||||||
|
goto err;
|
||||||
|
|
||||||
impl->requestPool.push_back(std::move(request));
|
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.
|
* video frame has to be addressed using more than one memory.
|
||||||
* address. Therefore, need calculate the number of discontiguous
|
* address. Therefore, need calculate the number of discontiguous
|
||||||
* memory and allocate the specified amount of memory */
|
* memory and allocate the specified amount of memory */
|
||||||
Stream *stream = impl->config->at(0).stream();
|
port->buffers_blocks = count_unique_fds(bufs.front()->planes());
|
||||||
const std::vector<std::unique_ptr<FrameBuffer>> &bufs =
|
if (port->buffers_blocks <= 0) {
|
||||||
impl->allocator->buffers(stream);
|
res = -ENOBUFS;
|
||||||
const std::vector<libcamera::FrameBuffer::Plane> &planes = bufs[0]->planes();
|
goto err;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buffers_blocks > 0) {
|
return 0;
|
||||||
port->buffers_blocks = buffers_blocks;
|
|
||||||
}
|
err:
|
||||||
|
freeBuffers(impl, port);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
void freeBuffers(struct impl *impl, struct port *port)
|
int spa_libcamera_clear_buffers(struct port *port)
|
||||||
{
|
{
|
||||||
impl->pendingRequests.clear();
|
for (std::size_t i = 0; i < port->n_buffers; i++) {
|
||||||
impl->requestPool.clear();
|
buffer *b = &port->buffers[i];
|
||||||
impl->allocator->free(port->streamConfig.stream());
|
spa_buffer *sb = b->outbuf;
|
||||||
}
|
|
||||||
|
|
||||||
int spa_libcamera_clear_buffers(struct impl *impl, struct port *port)
|
for (std::size_t j = 0; j < sb->n_datas; j++) {
|
||||||
{
|
auto *d = &sb->datas[j];
|
||||||
uint32_t i;
|
|
||||||
|
|
||||||
if (port->n_buffers == 0)
|
d->type = SPA_ID_INVALID;
|
||||||
return 0;
|
d->data = nullptr;
|
||||||
|
d->fd = -1;
|
||||||
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[0].type = SPA_ID_INVALID;
|
*b = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
freeBuffers(impl, port);
|
|
||||||
port->n_buffers = 0;
|
port->n_buffers = 0;
|
||||||
port->ring = SPA_RINGBUFFER_INIT();
|
|
||||||
|
|
||||||
return 0;
|
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 }
|
#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[] = {
|
const struct format_info format_info[] = {
|
||||||
/* RGB formats */
|
/* RGB formats */
|
||||||
|
MAKE_FMT(formats::R8, GRAY8, video, raw),
|
||||||
MAKE_FMT(formats::RGB565, RGB16, video, raw),
|
MAKE_FMT(formats::RGB565, RGB16, video, raw),
|
||||||
MAKE_FMT(formats::RGB565_BE, RGB16, video, raw),
|
MAKE_FMT(formats::RGB565_BE, RGB16, video, raw),
|
||||||
MAKE_FMT(formats::RGB888, BGR, 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)
|
void libcamera_on_fd_events(struct spa_source *source)
|
||||||
{
|
{
|
||||||
struct impl *impl = (struct impl*) source->data;
|
struct impl *impl = (struct impl*) source->data;
|
||||||
struct spa_io_buffers *io;
|
uint32_t index;
|
||||||
struct port *port = &impl->out_ports[0];
|
|
||||||
uint32_t index, buffer_id;
|
|
||||||
struct buffer *b;
|
|
||||||
uint64_t cnt;
|
uint64_t cnt;
|
||||||
|
|
||||||
if (source->rmask & SPA_IO_ERR) {
|
if (source->rmask & SPA_IO_ERR) {
|
||||||
|
|
@ -1032,35 +1161,13 @@ void libcamera_on_fd_events(struct spa_source *source)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spa_ringbuffer_get_read_index(&port->ring, &index) < 1) {
|
auto avail = spa_ringbuffer_get_read_index(&impl->completed_requests_rb, &index);
|
||||||
spa_log_error(impl->log, "nothing is queued");
|
for (; avail > 0; avail--, index++) {
|
||||||
return;
|
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,
|
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();
|
Stream *stream = impl->config->at(0).stream();
|
||||||
const std::vector<std::unique_ptr<FrameBuffer>> &bufs =
|
const std::vector<std::unique_ptr<FrameBuffer>> &bufs =
|
||||||
impl->allocator->buffers(stream);
|
impl->allocator.buffers(stream);
|
||||||
|
|
||||||
if (n_buffers > 0) {
|
if (n_buffers > 0) {
|
||||||
if (bufs.size() != n_buffers)
|
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)) {
|
if (d[0].type != SPA_ID_INVALID && d[0].type & (1u << SPA_DATA_DmaBuf)) {
|
||||||
port->memtype = 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;
|
port->memtype = SPA_DATA_MemFd;
|
||||||
} else if (d[0].type & (1u << SPA_DATA_MemPtr)) {
|
|
||||||
port->memtype = SPA_DATA_MemPtr;
|
|
||||||
} else {
|
} else {
|
||||||
spa_log_error(impl->log, "can't use buffers of type %d", d[0].type);
|
spa_log_error(impl->log, "can't use buffers of type %d", d[0].type);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
@ -1133,7 +1238,7 @@ spa_libcamera_alloc_buffers(struct impl *impl, struct port *port,
|
||||||
b = &port->buffers[i];
|
b = &port->buffers[i];
|
||||||
b->id = i;
|
b->id = i;
|
||||||
b->outbuf = buffers[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->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(
|
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();
|
d[j].fd = bufs[i]->planes()[j].fd.get();
|
||||||
spa_log_debug(impl->log, "Got fd = %" PRId64 " for buffer: #%d", d[j].fd, i);
|
spa_log_debug(impl->log, "Got fd = %" PRId64 " for buffer: #%d", d[j].fd, i);
|
||||||
d[j].data = nullptr;
|
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 {
|
} else {
|
||||||
spa_log_error(impl->log, "invalid buffer type");
|
spa_log_error(impl->log, "invalid buffer type");
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
spa_libcamera_buffer_recycle(impl, port, i);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
port->n_buffers = n_buffers;
|
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)
|
void impl::requestComplete(libcamera::Request *request)
|
||||||
{
|
{
|
||||||
struct impl *impl = this;
|
struct impl *impl = this;
|
||||||
struct port *port = &impl->out_ports[0];
|
uint32_t index;
|
||||||
Stream *stream = port->streamConfig.stream();
|
|
||||||
uint32_t index, buffer_id;
|
|
||||||
struct buffer *b;
|
|
||||||
|
|
||||||
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();
|
spa_ringbuffer_get_write_index(&impl->completed_requests_rb, &index);
|
||||||
b = &port->buffers[buffer_id];
|
impl->completed_requests[index & MASK_BUFFERS] = request;
|
||||||
|
spa_ringbuffer_write_update(&impl->completed_requests_rb, index + 1);
|
||||||
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);
|
|
||||||
|
|
||||||
if (spa_system_eventfd_write(impl->system, impl->source.fd, 1) < 0)
|
if (spa_system_eventfd_write(impl->system, impl->source.fd, 1) < 0)
|
||||||
spa_log_error(impl->log, "Failed to write on event fd");
|
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)
|
int spa_libcamera_stream_on(struct impl *impl)
|
||||||
{
|
{
|
||||||
struct port *port = &impl->out_ports[0];
|
struct port *port = &impl->out_ports[0];
|
||||||
|
|
@ -1316,76 +1337,60 @@ int spa_libcamera_stream_on(struct impl *impl)
|
||||||
if (impl->active)
|
if (impl->active)
|
||||||
return 0;
|
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);
|
res = spa_system_eventfd_create(impl->system, SPA_FD_CLOEXEC | SPA_FD_NONBLOCK);
|
||||||
if (res < 0)
|
if (res < 0)
|
||||||
return res;
|
goto err_stop;
|
||||||
|
|
||||||
impl->source.fd = res;
|
impl->source.fd = res;
|
||||||
impl->source.func = libcamera_on_fd_events;
|
impl->source.func = libcamera_on_fd_events;
|
||||||
impl->source.data = impl;
|
impl->source.data = impl;
|
||||||
impl->source.mask = SPA_IO_IN | SPA_IO_ERR;
|
impl->source.mask = SPA_IO_IN | SPA_IO_ERR;
|
||||||
impl->source.rmask = 0;
|
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());
|
for (auto& req : impl->requestPool) {
|
||||||
if ((res = impl->camera->start(&impl->initial_controls)) < 0)
|
req->reuse(libcamera::Request::ReuseFlag::ReuseBuffers);
|
||||||
goto err_remove_source;
|
|
||||||
|
|
||||||
impl->camera->requestCompleted.connect(impl, &impl::requestComplete);
|
if ((res = impl->camera->queueRequest(req.get())) < 0)
|
||||||
|
goto err_stop;
|
||||||
for (Request *req : impl->pendingRequests) {
|
|
||||||
if ((res = impl->camera->queueRequest(req)) < 0)
|
|
||||||
goto err_stop_camera;
|
|
||||||
}
|
}
|
||||||
impl->pendingRequests.clear();
|
|
||||||
|
|
||||||
impl->dll.bw = 0.0;
|
impl->dll.bw = 0.0;
|
||||||
impl->active = true;
|
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;
|
return 0;
|
||||||
|
|
||||||
err_stop_camera:
|
err_stop:
|
||||||
impl->camera->stop();
|
impl->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));
|
|
||||||
|
|
||||||
return res == -EACCES ? -EBUSY : res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
int spa_libcamera_stream_off(struct impl *impl)
|
int spa_libcamera_stream_off(struct impl *impl)
|
||||||
{
|
{
|
||||||
struct port *port = &impl->out_ports[0];
|
if (!impl->active)
|
||||||
int res;
|
|
||||||
|
|
||||||
if (!impl->active) {
|
|
||||||
for (std::unique_ptr<Request> &req : impl->requestPool)
|
|
||||||
req->reuse();
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
|
||||||
|
|
||||||
impl->active = false;
|
|
||||||
spa_log_info(impl->log, "stopping camera %s", impl->camera->id().c_str());
|
spa_log_info(impl->log, "stopping camera %s", impl->camera->id().c_str());
|
||||||
impl->pendingRequests.clear();
|
|
||||||
|
|
||||||
if ((res = impl->camera->stop()) < 0) {
|
impl->stop();
|
||||||
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);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -1824,21 +1829,24 @@ next:
|
||||||
int port_set_format(struct impl *impl, struct port *port,
|
int port_set_format(struct impl *impl, struct port *port,
|
||||||
uint32_t flags, const struct spa_pod *format)
|
uint32_t flags, const struct spa_pod *format)
|
||||||
{
|
{
|
||||||
struct spa_video_info info;
|
const bool try_only = SPA_FLAG_IS_SET(flags, SPA_NODE_PARAM_FLAG_TEST_ONLY);
|
||||||
int res;
|
|
||||||
|
if (!try_only) {
|
||||||
|
spa_libcamera_stream_off(impl);
|
||||||
|
spa_libcamera_clear_buffers(port);
|
||||||
|
freeBuffers(impl, port);
|
||||||
|
port->current_format.reset();
|
||||||
|
}
|
||||||
|
|
||||||
if (format == nullptr) {
|
if (format == nullptr) {
|
||||||
if (!port->current_format)
|
if (!try_only)
|
||||||
return 0;
|
spa_libcamera_close(impl);
|
||||||
|
|
||||||
spa_libcamera_stream_off(impl);
|
|
||||||
spa_libcamera_clear_buffers(impl, port);
|
|
||||||
port->current_format.reset();
|
|
||||||
|
|
||||||
spa_libcamera_close(impl);
|
|
||||||
goto done;
|
|
||||||
} else {
|
} else {
|
||||||
|
spa_video_info info;
|
||||||
|
int res;
|
||||||
|
|
||||||
spa_zero(info);
|
spa_zero(info);
|
||||||
|
|
||||||
if ((res = spa_format_parse(format, &info.media_type, &info.media_subtype)) < 0)
|
if ((res = spa_format_parse(format, &info.media_type, &info.media_subtype)) < 0)
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
|
|
@ -1854,54 +1862,32 @@ int port_set_format(struct impl *impl, struct port *port,
|
||||||
return -EINVAL;
|
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;
|
break;
|
||||||
case SPA_MEDIA_SUBTYPE_mjpg:
|
case SPA_MEDIA_SUBTYPE_mjpg:
|
||||||
if (spa_format_video_mjpg_parse(format, &info.info.mjpg) < 0)
|
if (spa_format_video_mjpg_parse(format, &info.info.mjpg) < 0)
|
||||||
return -EINVAL;
|
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;
|
break;
|
||||||
case SPA_MEDIA_SUBTYPE_h264:
|
case SPA_MEDIA_SUBTYPE_h264:
|
||||||
if (spa_format_video_h264_parse(format, &info.info.h264) < 0)
|
if (spa_format_video_h264_parse(format, &info.info.h264) < 0)
|
||||||
return -EINVAL;
|
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;
|
break;
|
||||||
default:
|
default:
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
res = spa_libcamera_set_format(impl, port, &info, try_only);
|
||||||
|
if (res < 0)
|
||||||
|
return res;
|
||||||
|
|
||||||
|
if (!try_only)
|
||||||
|
port->current_format = info;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (port->current_format && !(flags & SPA_NODE_PARAM_FLAG_TEST_ONLY)) {
|
if (try_only)
|
||||||
spa_libcamera_use_buffers(impl, port, nullptr, 0);
|
return 0;
|
||||||
port->current_format.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
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)) {
|
|
||||||
port->current_format = info;
|
|
||||||
}
|
|
||||||
|
|
||||||
done:
|
|
||||||
impl->info.change_mask |= SPA_NODE_CHANGE_MASK_PARAMS;
|
impl->info.change_mask |= SPA_NODE_CHANGE_MASK_PARAMS;
|
||||||
port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
|
port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
|
||||||
if (port->current_format) {
|
if (port->current_format) {
|
||||||
|
|
@ -1961,7 +1947,7 @@ int impl_node_port_use_buffers(void *object,
|
||||||
|
|
||||||
if (port->n_buffers) {
|
if (port->n_buffers) {
|
||||||
spa_libcamera_stream_off(impl);
|
spa_libcamera_stream_off(impl);
|
||||||
if ((res = spa_libcamera_clear_buffers(impl, port)) < 0)
|
if ((res = spa_libcamera_clear_buffers(port)) < 0)
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
if (n_buffers > 0 && !port->current_format)
|
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}},
|
out_ports{{this}},
|
||||||
manager(std::move(manager)),
|
manager(std::move(manager)),
|
||||||
camera(std::move(camera)),
|
camera(std::move(camera)),
|
||||||
config(std::move(config))
|
config(std::move(config)),
|
||||||
|
allocator(this->camera)
|
||||||
{
|
{
|
||||||
libcamera_log_topic_init(log);
|
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_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);
|
result = gst_base_src_set_caps (GST_BASE_SRC (pwsrc), negotiated_caps);
|
||||||
if (!result)
|
if (!result)
|
||||||
goto no_caps;
|
goto no_caps;
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@
|
||||||
* ## Module Options
|
* ## Module Options
|
||||||
*
|
*
|
||||||
* - `jack.library`: the libjack to load, by default libjack.so.0 is searched in
|
* - `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.
|
* Can be an absolute path.
|
||||||
* - `jack.server`: the name of the JACK server to tunnel to.
|
* - `jack.server`: the name of the JACK server to tunnel to.
|
||||||
* - `jack.client-name`: the name of the JACK client.
|
* - `jack.client-name`: the name of the JACK client.
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ pwtest_deps = [
|
||||||
pipewire_dep,
|
pipewire_dep,
|
||||||
mathlib,
|
mathlib,
|
||||||
dl_lib,
|
dl_lib,
|
||||||
cap_lib,
|
|
||||||
epoll_shim_dep
|
epoll_shim_dep
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,9 +19,6 @@
|
||||||
#ifdef HAVE_PIDFD_OPEN
|
#ifdef HAVE_PIDFD_OPEN
|
||||||
#include <sys/syscall.h>
|
#include <sys/syscall.h>
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_LIBCAP
|
|
||||||
#include <sys/capability.h>
|
|
||||||
#endif
|
|
||||||
#include <sys/epoll.h>
|
#include <sys/epoll.h>
|
||||||
#include <sys/ptrace.h>
|
#include <sys/ptrace.h>
|
||||||
#include <sys/resource.h>
|
#include <sys/resource.h>
|
||||||
|
|
@ -33,6 +30,7 @@
|
||||||
#include <valgrind/valgrind.h>
|
#include <valgrind/valgrind.h>
|
||||||
|
|
||||||
#include "spa/utils/ansi.h"
|
#include "spa/utils/ansi.h"
|
||||||
|
#include "spa/utils/cleanup.h"
|
||||||
#include "spa/utils/string.h"
|
#include "spa/utils/string.h"
|
||||||
#include "spa/utils/defs.h"
|
#include "spa/utils/defs.h"
|
||||||
#include "spa/utils/list.h"
|
#include "spa/utils/list.h"
|
||||||
|
|
@ -1298,39 +1296,20 @@ static void list_tests(struct pwtest_context *ctx)
|
||||||
|
|
||||||
static bool is_debugger_attached(void)
|
static bool is_debugger_attached(void)
|
||||||
{
|
{
|
||||||
bool rc = false;
|
spa_autofree char *line = NULL;
|
||||||
#ifdef HAVE_LIBCAP
|
size_t length = 0;
|
||||||
int status;
|
|
||||||
int pid = fork();
|
|
||||||
|
|
||||||
if (pid == -1)
|
spa_autoptr(FILE) f = fopen("/proc/self/status", "re");
|
||||||
return 0;
|
if (!f)
|
||||||
|
return false;
|
||||||
|
|
||||||
if (pid == 0) {
|
while (getline(&line, &length, f) >= 0) {
|
||||||
int ppid = getppid();
|
unsigned int tracer_pid;
|
||||||
cap_t caps = cap_get_pid(ppid);
|
if (sscanf(line, "TracerPid: %u", &tracer_pid) == 1)
|
||||||
cap_flag_value_t cap_val;
|
return tracer_pid > 0;
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
return false;
|
||||||
return !!rc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void usage(FILE *fp, const char *progname)
|
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);
|
size_t size = pwtest_get_iteration(current_test);
|
||||||
char dest[8];
|
char dest[8];
|
||||||
|
|
||||||
if (RUNNING_ON_VALGRIND)
|
|
||||||
return PWTEST_SKIP;
|
|
||||||
|
|
||||||
spa_scnprintf(dest, size, "1234"); /* expected to abort() */
|
spa_scnprintf(dest, size, "1234"); /* expected to abort() */
|
||||||
|
|
||||||
return PWTEST_FAIL;
|
return PWTEST_FAIL;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue