diff --git a/README.md b/README.md index 75f178f..23d596e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# UxPlay 1.37 +# UxPlay 1.38 This project is a GPLv3 unix AirPlay server which now also works on macOS. Its main use is to act like an AppleTV for screen-mirroring (with audio) of iOS/macOS clients @@ -21,7 +21,7 @@ If the client streams audio using AirPlay as opposed to AirPlay screen-mirrorin input into the GStreamer audio-rendering pipeline, but does not get rendered into audio output. If someone can adapt the GStreamer audio pipeline to also render these Airplay audio streams, such an enhancement of UxPlay would be welcome as a Pull Request!_ -UxPlay 1.37 is based on https://github.com/FD-/RPiPlay, with GStreamer integration from +UxPlay 1.38 is based on https://github.com/FD-/RPiPlay, with GStreamer integration from https://github.com/antimof/UxPlay. (UxPlay only uses GStreamer, and does not contain the alternative Raspberry-Pi-specific audio and video renderers also found in RPiPlay.) @@ -198,6 +198,11 @@ plugin for Intel graphics is *NOT* installed (**uninstall it** if it is installe "-vs ximagesink" or "-vs xvimagesink", to see if this fixes the problem, or "-vs vaapisink" to see if this reproduces the problem.) +You can try to fix audio problems by using the "-as _audiosink_" option to choose the GStreamer audiosink , rather than +have autoaudiosink pick one for you. The command "gst_inspect-1.0 | grep Sink | grep Audio" " will show you which audiosinks and videosinks are +available on your system. (Replace "Audio" by "Video" to see videosinks). Common audiosinks are pulsesink, alsasink, osssink, oss4sink, +and osxaudiosink (macOS). + If you ran cmake with "-DZOOMFIX=ON", check if the problem is still there without ZOOMFIX. ZOOMFIX is only applied to the default videosink choice ("autovideosink") and the two X11 videosinks "ximagesink" and "xvimagesink". ZOOMFIX is only designed for these last two; if @@ -269,7 +274,7 @@ which will not work if a firewall is running. attempt to run two instances of uxplay on the same computer.) On macOS, random MAC addresses are always used. -**-a** disable audio, leaving only the video playing. + Also: image transforms that had been added to RPiPlay have been ported to UxPlay: @@ -290,6 +295,14 @@ Also: image transforms that had been added to RPiPlay have been ported to UxPlay **-vs 0** suppresses display of streamed video, but plays streamed audio. (The client's screen is still mirrored at a reduced rate of 1 frame per second, but is not rendered or displayed.) +**-as _audiosink_** chooses the GStreamer audiosink, instead of letting + autoaudiosink pick it for you. Some audiosink choices are: pulsesink, alsasink, + osssink, oss4sink, and osxaudiosink (for macOS). Using quotes + "..." might allow some parameters to be included with the audiosink name. + (Some choices of audiosink might not work on your system.) + +**-as 0** or **-a** suppresses playing of streamed audio, but displays streamed video. + **-t _timeout_** will cause the server to relaunch (without stopping uxplay) if no connections have been present during the previous _timeout_ seconds. (You may wish to use this because the Server may not be visible to new Clients that were inactive when the Server was launched, and an idle Bonjour @@ -302,6 +315,8 @@ Also: image transforms that had been added to RPiPlay have been ported to UxPlay # ChangeLog +1.38 2021-10-8 Add -as _audiosink_ option to allow user to choose the GStreamer audiosink. + 1.37 2021-09-29 Append "@hostname" to AirPlay Server name, where "hostname" is the name of the server running uxplay (reworked change in 1.36). @@ -330,7 +345,7 @@ This involved crypto updates, replacement of the included plist library by the system-installed version, and a change over to a library llhttp for http parsing. -2. Added the -s, -o -p, -m, -r, -f, -fps -vs and -t options. +2. Added the -s, -o -p, -m, -r, -f, -fps -vs -va and -t options. 3. If "`cmake -DZOOMFIX=ON .`" is run before compiling, the mirrored window is now visible to screen-sharing applications such as diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 0761f43..e6cb24b 100755 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -23,7 +23,7 @@ add_library( airplay find_package(PkgConfig REQUIRED) if( UNIX AND NOT APPLE ) - find_library( LIBPLIST NAMES plist plist-2.0 ) + find_library( LIBPLIST NAMES plist3 plist plist-2.0 ) elseif( APPLE ) pkg_check_modules( PLIST REQUIRED libplist-2.0 ) find_library( LIBPLIST libplist-2.0.a REQUIRED ) diff --git a/renderers/audio_renderer.h b/renderers/audio_renderer.h index 4f15d40..ad542d8 100644 --- a/renderers/audio_renderer.h +++ b/renderers/audio_renderer.h @@ -32,7 +32,7 @@ extern "C" { typedef struct audio_renderer_s audio_renderer_t; -audio_renderer_t *audio_renderer_init(logger_t *logger, video_renderer_t *video_renderer); + audio_renderer_t *audio_renderer_init(logger_t *logger, video_renderer_t *video_renderer, const char* audiosink); void audio_renderer_start(audio_renderer_t *renderer); void audio_renderer_render_buffer(audio_renderer_t *renderer, raop_ntp_t *ntp, unsigned char* data, int data_len, uint64_t pts); void audio_renderer_set_volume(audio_renderer_t *renderer, float volume); diff --git a/renderers/audio_renderer_gstreamer.c b/renderers/audio_renderer_gstreamer.c index 00f5e8c..20b6ccd 100644 --- a/renderers/audio_renderer_gstreamer.c +++ b/renderers/audio_renderer_gstreamer.c @@ -51,7 +51,7 @@ static gboolean check_plugins (void) return ret; } -audio_renderer_t *audio_renderer_init(logger_t *logger, video_renderer_t *video_renderer) { +audio_renderer_t *audio_renderer_init(logger_t *logger, video_renderer_t *video_renderer, const char* audiosink) { audio_renderer_t *renderer; GError *error = NULL; @@ -63,8 +63,11 @@ audio_renderer_t *audio_renderer_init(logger_t *logger, video_renderer_t *video_ assert(check_plugins ()); - renderer->pipeline = gst_parse_launch("appsrc name=audio_source stream-type=0 format=GST_FORMAT_TIME is-live=true ! queue ! decodebin !" - "audioconvert ! volume name=volume ! level ! autoaudiosink sync=false", &error); + GString *launch = g_string_new("appsrc name=audio_source stream-type=0 format=GST_FORMAT_TIME is-live=true ! queue ! decodebin !" + "audioconvert ! volume name=volume ! level ! "); + g_string_append(launch, audiosink); + g_string_append(launch, " sync=false"); + renderer->pipeline = gst_parse_launch(launch->str, &error); g_assert (renderer->pipeline); renderer->appsrc = gst_bin_get_by_name (GST_BIN (renderer->pipeline), "audio_source"); diff --git a/uxplay.cpp b/uxplay.cpp index dae2019..60e6e0d 100755 --- a/uxplay.cpp +++ b/uxplay.cpp @@ -35,7 +35,7 @@ #include "renderers/video_renderer.h" #include "renderers/audio_renderer.h" -#define VERSION "1.37" +#define VERSION "1.38" #define DEFAULT_NAME "UxPlay" #define DEFAULT_DEBUG_LOG false @@ -44,7 +44,7 @@ static int start_server (std::vector hw_addr, std::string name, unsigned short display[5], unsigned short tcp[3], unsigned short udp[3], videoflip_t videoflip[2], - bool use_audio, bool debug_log, std::string videosink); + bool use_audio, bool debug_log, std::string videosink, std::string audiosink); static int stop_server (); @@ -159,13 +159,15 @@ static void print_info (char *name) { printf(" use \"-p n1,n2,n3\" to set each port, \"n1,n2\" for n3 = n2+1\n"); printf(" \"-p tcp n\" or \"-p udp n\" sets TCP or UDP ports only\n"); printf("-m Use random MAC address (use for concurrent UxPlay's)\n"); - printf("-a Turn audio off. video output only\n"); printf("-t n Relaunch server if no connection existed in last n seconds\n"); - printf("-vs Choose the GStreamer videosink; default \"autovideosink\"\n"); - printf(" choices: ximagesink,xvimagesink,vaapisink,fpsdisplaysink, etc.\n"); + printf("-vs Choose the GStreamer videosink; default \"autovideosink\"\n"); + printf(" choices: ximagesink,xvimagesink,vaapisink,glimagesink, etc.\n"); printf("-vs 0 Streamed audio only, with no video display window\n"); + printf("-as Choose the GStreamer audiosink; default \"autoaudiosink\"\n"); + printf(" choices: pulsesink,alsasink,osssink,oss4sink,osxaudiosink,etc.\n"); + printf("-as 0 (or -a) Turn audio off, video output only\n"); printf("-d Enable debug logging\n"); - printf("-v/-h Displays this help and version information\n"); + printf("-v or -h Displays this help and version information\n"); } bool option_has_value(const int i, const int argc, std::string option, const char *next_arg) { @@ -296,7 +298,8 @@ int main (int argc, char *argv[]) { unsigned short display[5] = {0}, tcp[3] = {0}, udp[3] = {0}; videoflip_t videoflip[2] = { NONE , NONE }; std::string videosink = "autovideosink"; - + std::string audiosink = "autoaudiosink"; + #ifdef SUPPRESS_AVAHI_COMPAT_WARNING // suppress avahi_compat nag message. avahi emits a "nag" warning (once) // if getenv("AVAHI_COMPAT_NOWARN") returns null. @@ -373,6 +376,10 @@ int main (int argc, char *argv[]) { if (!option_has_value(i, argc, arg, argv[i+1])) exit(1); videosink.erase(); videosink.append(argv[++i]); + } else if (arg == "-as") { + if (!option_has_value(i, argc, arg, argv[i+1])) exit(1); + audiosink.erase(); + audiosink.append(argv[++i]); } else if (arg == "-t") { if (!option_has_value(i, argc, argv[i], argv[i+1])) exit(1); server_timeout = 0; @@ -401,7 +408,7 @@ int main (int argc, char *argv[]) { relaunch: connections_stopped = false; if (start_server(server_hw_addr, server_name, display, tcp, udp, - videoflip,use_audio, debug_log, videosink)) { + videoflip,use_audio, debug_log, videosink, audiosink)) { return 1; } @@ -483,7 +490,7 @@ extern "C" void log_callback (void *cls, int level, const char *msg) { int start_server (std::vector hw_addr, std::string name, unsigned short display[5], unsigned short tcp[3], unsigned short udp[3], videoflip_t videoflip[2], - bool use_audio, bool debug_log, std::string videosink) { + bool use_audio, bool debug_log, std::string videosink, std::string audiosink) { raop_callbacks_t raop_cbs; memset(&raop_cbs, 0, sizeof(raop_cbs)); raop_cbs.conn_init = conn_init; @@ -507,6 +514,10 @@ int start_server (std::vector hw_addr, std::string name, unsigned short di use_video = false; display[3] = 1; /* set fps to 1 frame per sec when no video will be shown */ } + if(audiosink == "0") { + use_audio = false; + } + raop_set_display(raop, display[0], display[1], display[2], display[3], display[4]); /* network port selection (ports listed as "0" will be dynamically assigned) */ @@ -533,7 +544,7 @@ int start_server (std::vector hw_addr, std::string name, unsigned short di if (! use_audio) { LOGI("Audio disabled"); - } else if ((audio_renderer = audio_renderer_init(render_logger, video_renderer)) == + } else if ((audio_renderer = audio_renderer_init(render_logger, video_renderer, audiosink.c_str())) == NULL) { LOGE("Could not init audio renderer"); stop_server();