diff --git a/CMakeLists.txt b/CMakeLists.txt index fa6d34f..75f3022 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,13 +4,16 @@ project(uxplay) set (CMAKE_CXX_STANDARD 11) -add_definitions(-DAVAHI_COMPAT_NOWARN) +if (ZOOMFIX) add_definitions(-DX_DISPLAY_FIX) find_package(X11 REQUIRED) link_libraries(${X11_LIBRARIES}) include_directories(${X11_INCLUDE_DIR}) # link_directories(${X11_LIBRARIES}) +endif (ZOOMFIX) + +add_definitions(-DAVAHI_COMPAT_NOWARN) add_subdirectory(lib/llhttp) add_subdirectory(lib/playfair) @@ -20,5 +23,5 @@ add_subdirectory(renderers) add_executable( uxplay uxplay.cpp) target_link_libraries ( uxplay renderers airplay ${X11_LIBRARIES}) -install(PROGRAMS uxplay DESTINATION bin) +install(TARGETS uxplay RUNTIME DESTINATION bin) diff --git a/renderers/video_renderer.h b/renderers/video_renderer.h index 0cd08dd..8f03e2a 100644 --- a/renderers/video_renderer.h +++ b/renderers/video_renderer.h @@ -33,10 +33,18 @@ extern "C" { #include "../lib/logger.h" #include "../lib/raop_ntp.h" +typedef enum videoflip_e { + NONE, + LEFT, + RIGHT, + INVERT, + VFLIP, + HFLIP, +} videoflip_t; typedef struct video_renderer_s video_renderer_t; -video_renderer_t *video_renderer_init(logger_t *logger, const char *server_name); +video_renderer_t *video_renderer_init(logger_t *logger, const char *server_name, videoflip_t videoflip); void video_renderer_start(video_renderer_t *renderer); void video_renderer_render_buffer(video_renderer_t *renderer, raop_ntp_t *ntp, unsigned char* data, int data_len, uint64_t pts, int type); void video_renderer_flush(video_renderer_t *renderer); diff --git a/renderers/video_renderer_gstreamer.c b/renderers/video_renderer_gstreamer.c index 1ef0a74..2e1c2fc 100644 --- a/renderers/video_renderer_gstreamer.c +++ b/renderers/video_renderer_gstreamer.c @@ -53,7 +53,7 @@ static gboolean check_plugins (void) return ret; } -video_renderer_t *video_renderer_init(logger_t *logger, const char *server_name) { +video_renderer_t *video_renderer_init(logger_t *logger, const char *server_name, videoflip_t videoflip) { video_renderer_t *renderer; GError *error = NULL; @@ -71,8 +71,29 @@ video_renderer_t *video_renderer_init(logger_t *logger, const char *server_name) assert(check_plugins ()); - renderer->pipeline = gst_parse_launch("appsrc name=video_source stream-type=0 format=GST_FORMAT_TIME is-live=true !" - "queue ! decodebin ! videoconvert ! autovideosink name=video_sink sync=false", &error); + GString *launch = g_string_new("appsrc name=video_source stream-type=0 format=GST_FORMAT_TIME is-live=true !" + "queue ! decodebin ! videoconvert ! "); + + /* image transform */ + switch (videoflip) { + case LEFT: + g_string_append(launch, "videoflip method=counterclockwise ! "); + break; + case RIGHT: + g_string_append(launch, "videoflip method=clockwise ! "); + break; + case INVERT: + g_string_append(launch, "videoflip method=rotate-180 ! "); + break; + case HFLIP: + g_string_append(launch, "videoflip method=horizontal-flip ! "); + break; + case VFLIP: + g_string_append(launch, "videoflip method=vertical-flip ! "); + } + + g_string_append(launch, "autovideosink name=video_sink 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), "video_source"); diff --git a/uxplay.cpp b/uxplay.cpp index bfaa65b..f6bd77a 100755 --- a/uxplay.cpp +++ b/uxplay.cpp @@ -44,7 +44,7 @@ int start_server(std::vector hw_addr, std::string name, unsigned short display_size[2], - unsigned short tcp[2], unsigned short udp[3], + unsigned short tcp[2], unsigned short udp[3], videoflip_t videoflip, bool use_audio, bool debug_log); int stop_server(); @@ -120,8 +120,9 @@ void print_info(char *name) { printf("UxPlay %s: An open-source AirPlay mirroring server based on RPiPlay\n", VERSION); printf("Usage: %s [-n name] [-s wxh] [-p [n]]\n", name); printf("Options:\n"); - printf("-n name Specify the network name of the AirPlay server\n"); - printf("-s wxh Set display size: width w height h default 1920x1080\n"); + printf("-n name Specify the network name of the AirPlay server\n"); + printf("-s wxh Set display size: width w height h default 1920x1080\n"); + printf("-f {H|V|R|L|I} horizontal/vertical flip; rotate 90R/90L/180 deg\n"); printf("-p n Use fixed UDP+TCP network ports n:n+1:n+2 > 1023\n"); printf("-p Use legacy UDP 6000:6001:7011 TCP 7000:7001:7100\n"); printf("-r use random MAC address (use for concurrent UxPlay's)\n"); @@ -159,6 +160,33 @@ bool get_lowest_port(char *str, unsigned short *n) { return true; } +bool get_videoflip(char *str, videoflip_t *videoflip) { + char c = str[0]; + printf(" %c |%s| %d\n",c, str, strlen(str)); + + if(strlen(str) > 1) return false; + printf(" %c |%s| %d\n",c, str, strlen(str)); + switch (c) { + case 'L': + *videoflip = LEFT; + break; + case 'R': + *videoflip = RIGHT; + break; + case 'I': + *videoflip = INVERT; + break; + case 'H': + *videoflip = HFLIP; + break; + case 'V': + *videoflip = VFLIP; + break; + default: + return false; + } + return true; +} int main(int argc, char *argv[]) { init_signals(); @@ -169,7 +197,8 @@ int main(int argc, char *argv[]) { bool debug_log = DEFAULT_DEBUG_LOG; unsigned short display_size[2] = { (unsigned short) DEFAULT_DISPLAY_WIDTH, (unsigned short) DEFAULT_DISPLAY_HEIGHT }; unsigned short tcp[2] = {0}, udp[3] = {0}; - + videoflip_t videoflip = NONE; + #ifdef AVAHI_COMPAT_NOWARN //suppress avahi_compat nag message char avahi_compat_nowarn[] = "AVAHI_COMPAT_NOWARN==1"; @@ -187,12 +216,21 @@ int main(int argc, char *argv[]) { fprintf(stderr,"invalid \"-s\" had no argument\n"); exit(1); } - std::string value(argv[++i]); + std::string value(argv[++i]); if (!get_display_size(argv[i], &display_size[0], &display_size[1])) { fprintf(stderr, "invalid \"-s %s\"; default is \"-s 1920x1080\" (< 5 digits)\n", value.c_str()); exit(1); } + } else if (arg == "-f") { + if (i == argc - 1 || argv[i + 1][0] == '-') { + fprintf(stderr,"invalid \"f\" had no argument\n"); + exit(1); + } + if (!get_videoflip(argv[++i], &videoflip)) { + fprintf(stderr,"invalid \"-f %s\" , unknown videoflip type\n",argv[i]); + exit(1); + } } else if (arg == "-p") { if (i == argc - 1 || argv[i + 1][0] == '-') { tcp[0] = 7100; tcp[1] = 7000; @@ -220,7 +258,7 @@ int main(int argc, char *argv[]) { print_info(argv[0]); exit(0); } else { - LOGI("unknown option %s, skipping\n",argv[i]); + LOGI("unknown option %s, skipping\n",argv[i]); } } @@ -237,7 +275,8 @@ int main(int argc, char *argv[]) { parse_hw_addr(mac_address, server_hw_addr); - if (start_server(server_hw_addr, server_name, display_size, tcp, udp, use_audio, debug_log) != 0) { + if (start_server(server_hw_addr, server_name, display_size, tcp, udp, videoflip, + use_audio, debug_log) != 0) { return 1; } running = true; @@ -307,7 +346,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_size[2], - unsigned short tcp[2], unsigned short udp[3], + unsigned short tcp[2], unsigned short udp[3], videoflip_t videoflip, bool use_audio, bool debug_log) { raop_callbacks_t raop_cbs; memset(&raop_cbs, 0, sizeof(raop_cbs)); @@ -332,7 +371,7 @@ int start_server(std::vector hw_addr, std::string name, unsigned short dis logger_set_callback(render_logger, log_callback, NULL); logger_set_level(render_logger, debug_log ? LOGGER_DEBUG : LOGGER_INFO); - if ((video_renderer = video_renderer_init(render_logger, name.c_str())) == NULL) { + if ((video_renderer = video_renderer_init(render_logger, name.c_str(), videoflip)) == NULL) { LOGE("Could not init video renderer"); return -1; } @@ -351,7 +390,7 @@ int start_server(std::vector hw_addr, std::string name, unsigned short dis /* write desired display pixel width, pixel height to raop (use 0 for default values) */ raop_set_display_size(raop, display_size[0], display_size[1]); - /* network port selection (use value 0 for dynamic assignment) */ + /* network port selection (ports listed as "0" will dynamically assigned) */ raop_set_tcp_ports(raop, tcp); raop_set_udp_ports(raop, udp);