From f9e541eee227e7119fbca5331eb10305d88e70c9 Mon Sep 17 00:00:00 2001 From: Christian Glombek Date: Sat, 23 Sep 2023 20:32:09 +0200 Subject: [PATCH] module-raop-sink: Start with GET /info --- meson.build | 2 + src/modules/meson.build | 6 +-- src/modules/module-raop-sink.c | 82 +++++++++++++++++++++++++++++++++- 3 files changed, 85 insertions(+), 5 deletions(-) diff --git a/meson.build b/meson.build index ff09002cb..4aaa7c601 100644 --- a/meson.build +++ b/meson.build @@ -275,6 +275,8 @@ summary({'dbus (Bluetooth, rt, portal, pw-reserve)': dbus_dep.found()}, bool_yn: cdata.set('HAVE_DBUS', dbus_dep.found()) sdl_dep = dependency('sdl2', required : get_option('sdl2')) summary({'SDL2 (video examples)': sdl_dep.found()}, bool_yn: true, section: 'Misc dependencies') +plist_lib = dependency('libplist-2.0', required: get_option('raop')) +summary({'libplist (RAOP)': plist_lib.found()}, bool_yn: true, section: 'Misc dependencies') drm_dep = dependency('libdrm', required : false) readline_dep = dependency('readline', required : get_option('readline')) diff --git a/src/modules/meson.build b/src/modules/meson.build index 3f0d6edd7..914986b94 100644 --- a/src/modules/meson.build +++ b/src/modules/meson.build @@ -603,7 +603,7 @@ if build_module_raop_discover endif summary({'raop-discover (needs Avahi)': build_module_raop_discover}, bool_yn: true, section: 'Optional Modules') -build_module_raop = openssl_lib.found() +build_module_raop = openssl_lib.found() and plist_lib.found() if build_module_raop pipewire_module_raop_sink = shared_library('pipewire-module-raop-sink', [ 'module-raop-sink.c', @@ -612,10 +612,10 @@ if build_module_raop install : true, install_dir : modules_install_dir, install_rpath: modules_install_dir, - dependencies : [mathlib, dl_lib, rt_lib, pipewire_dep, openssl_lib], + dependencies : [mathlib, dl_lib, rt_lib, pipewire_dep, openssl_lib, plist_lib], ) endif -summary({'raop-sink (requires OpenSSL)': build_module_raop}, bool_yn: true, section: 'Optional Modules') +summary({'raop-sink (requires OpenSSL, plist)': build_module_raop}, bool_yn: true, section: 'Optional Modules') roc_dep = dependency('roc', required: get_option('roc')) summary({'ROC': roc_dep.found()}, bool_yn: true, section: 'Streaming between daemons') diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c index 9d6b8ee61..19eaef6d7 100644 --- a/src/modules/module-raop-sink.c +++ b/src/modules/module-raop-sink.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #if OPENSSL_API_LEVEL >= 30000 @@ -29,6 +30,7 @@ #include "config.h" +#include #include #include #include @@ -121,6 +123,10 @@ PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME); #define PW_LOG_TOPIC_DEFAULT mod_topic +SPA_DEFINE_AUTO_CLEANUP(plist, plist_t, { + spa_clear_ptr(*thing, plist_free); +}) + #define FRAMES_PER_TCP_PACKET 4096 #define FRAMES_PER_UDP_PACKET 352 @@ -193,6 +199,39 @@ enum { CODEC_AAC_ELD, }; +// https://openairplay.github.io/airplay-spec/status_flags.html +enum { + AP_STATUS_FLAG_PIN_REQUIRED = (1 << 3), + AP_STATUS_FLAG_PASSWORD_REQUIRED = (1 << 7), + AP_STATUS_FLAG_ONE_TIME_PAIRING_REQUIRED = (1 << 9), +}; + +// https://emanuelecozzi.net/docs/airplay2/features/ +enum { + AP_FEATURE_FLAG_VIDEO_V1 = 1llu, + AP_FEATURE_FLAG_PHOTO = (1llu << 1), + AP_FEATURE_FLAG_SLIDESHOW = (1llu << 5), + AP_FEATURE_FLAG_SCREEN = (1llu << 7), + AP_FEATURE_FLAG_AUDIO = (1llu << 9), + AP_FEATURE_FLAG_AUTHENTICATION_4 = (1llu << 14), + AP_FEATURE_FLAG_FORMAT_PCM = (1llu << 18), // CODEC_PCM + AP_FEATURE_FLAG_FORMAT_ALAC = (1llu << 19), // CODEC_ALAC + AP_FEATURE_FLAG_FORMAT_AAC = (1llu << 20), // CODEC_AAC + AP_FEATURE_FLAG_FORMAT_AAC_ELD = (1llu << 21), // CODEC_AAC_ELD + AP_FEATURE_FLAG_AUTHENTICATION_1 = (1llu << 23), // CRYPTO_RSA + AP_FEATURE_FLAG_AUTHENTICATION_8 = (1llu << 26), // CRYPTO_AUTH_SETUP + AP_FEATURE_FLAG_PAIR_LEGACY = (1llu << 27), // CRYPTO_AUTH_SETUP + AP_FEATURE_FLAG_RAOP = (1llu << 30), // CRYPTO_AUTH_SETUP + AP_FEATURE_FLAG_PAIR_COREUTILS = (1llu << 38), // CRYPTO_PAIR_SETUP + AP_FEATURE_FLAG_BUFFERED_AUDIO = (1llu << 40), + AP_FEATURE_FLAG_PTP = (1llu << 41), + AP_FEATURE_FLAG_PAIR_SYSTEM = (1llu << 43), // CRYPTO_PAIR_SETUP + AP_FEATURE_FLAG_PAIR_HOMEKIT = (1llu << 46), // CRYPTO_PAIR_SETUP + AP_FEATURE_FLAG_PAIR_TRANSIENT = (1llu << 48), // CRYPTO_PAIR_SETUP + AP_FEATURE_FLAG_VIDEO_V2 = (1llu << 49), + AP_FEATURE_FLAG_PAIR_SETUP = (1llu << 51), // CRYPTO_PAIR_SETUP +}; + struct impl { struct pw_context *context; @@ -1539,6 +1578,46 @@ static int rtsp_options_reply(void *data, int status, const struct spa_dict *hea return res; } +static int rtsp_do_options(void *data) +{ + struct impl *impl = data; + + return pw_rtsp_client_send(impl->rtsp, "OPTIONS", &impl->headers->dict, + NULL, NULL, rtsp_options_reply, impl); +} + +static int rtsp_get_info_reply(void *data, int status, const struct spa_dict *headers, const struct pw_array *content) +{ + struct impl *impl = data; + uint64_t features = 0, status_flags = 0; + spa_auto(plist) info_plist = NULL; + plist_t features_plist = NULL, status_flags_plist = NULL; + + pw_log_info("info status: %d", status); + + plist_from_bin(content->data, content->size, &info_plist); + + status_flags_plist = plist_dict_get_item(info_plist, "statusFlags"); + plist_get_uint_val(status_flags_plist, &status_flags); + if (status_flags != 0) + pw_log_info("received statusFlags value: 0x%" PRIx64 "\n", status_flags); + + features_plist = plist_dict_get_item(info_plist, "features"); + plist_get_uint_val(features_plist, &features); + if (features != 0) + pw_log_info("received features value: 0x%" PRIx64 "\n", features); + + return rtsp_do_options(impl); +} + +static int rtsp_do_get_info(void *data) +{ + struct impl *impl = data; + + return pw_rtsp_client_url_send(impl->rtsp, "/info", "GET", &impl->headers->dict, + NULL, NULL, 0, rtsp_get_info_reply, impl); +} + static void rtsp_connected(void *data) { struct impl *impl = data; @@ -1562,8 +1641,7 @@ static void rtsp_connected(void *data) pw_properties_set(impl->headers, "User-Agent", DEFAULT_USER_NAME "/" PACKAGE_VERSION); - pw_rtsp_client_send(impl->rtsp, "OPTIONS", &impl->headers->dict, - NULL, NULL, rtsp_options_reply, impl); + rtsp_do_get_info(data); } static void connection_cleanup(struct impl *impl)