X-Git-Url: https://gerrit.automotivelinux.org/gerrit/gitweb?a=blobdiff_plain;f=app%2Fmain.cpp;h=64a435b319b79b83769986e5e1df6a08d85b55cb;hb=refs%2Fheads%2Fquillback;hp=1acb70fde020f62d11606a61dc7cee3adcfe4e54;hpb=714ad7e26b9860360fb8098613f8b4887d851e12;p=apps%2Fcamera-gstreamer.git diff --git a/app/main.cpp b/app/main.cpp index 1acb70f..64a435b 100644 --- a/app/main.cpp +++ b/app/main.cpp @@ -14,15 +14,24 @@ #include #include -#include "xdg-shell-client-protocol.h" -#include "agl-shell-desktop-client-protocol.h" #include "utils.h" +#include "xdg-shell-client-protocol.h" +#include "AglShellGrpcClient.h" #include #include #include +#if !GST_CHECK_VERSION(1, 22, 0) +#define gst_is_wl_display_handle_need_context_message gst_is_wayland_display_handle_need_context_message +#define gst_wl_display_handle_context_new gst_wayland_display_handle_context_new +#endif + +#ifndef APP_DATA_PATH +#define APP_DATA_PATH /usr/share/applications/data +#endif + // these only applies if the window is a dialog/pop-up one // by default the compositor make the window maximized #define WINDOW_WIDTH_SIZE 640 @@ -31,6 +40,8 @@ #define WINDOW_WIDTH_POS_X 640 #define WINDOW_WIDTH_POS_Y 180 +#define MAX_BUFFER_ALLOC 2 + // C++ requires a cast and we in wayland we do the cast implictly #define WL_ARRAY_FOR_EACH(pos, array, type) \ for (pos = (type)(array)->data; \ @@ -50,8 +61,6 @@ struct display { } output_data; struct xdg_wm_base *wm_base; - struct agl_shell_desktop *agl_shell_desktop; - int has_xrgb; }; @@ -59,6 +68,9 @@ struct buffer { struct wl_buffer *buffer; void *shm_data; int busy; + int width, height; + size_t size; /* width * 4 * height */ + struct wl_list buffer_link; /** window::buffer_list */ }; struct window { @@ -66,6 +78,8 @@ struct window { int x, y; int width, height; + int init_width; + int init_height; struct wl_surface *surface; @@ -75,8 +89,9 @@ struct window { int fullscreen, maximized; - struct buffer buffers[2]; + struct wl_list buffer_list; struct wl_callback *callback; + bool needs_update_buffer; }; @@ -84,15 +99,68 @@ struct receiver_data { struct window *window; GstElement *pipeline; - GstWaylandVideo *wl_video; GstVideoOverlay *overlay; }; static int running = 1; +static bool gst_pipeline_failed = FALSE; +static bool fallback_gst_pipeline_tried = FALSE; static void redraw(void *data, struct wl_callback *callback, uint32_t time); +static struct buffer * +alloc_buffer(struct window *window, int width, int height) +{ + struct buffer *buffer = static_cast(calloc(1, sizeof(*buffer))); + + buffer->width = width; + buffer->height = height; + wl_list_insert(&window->buffer_list, &buffer->buffer_link); + + return buffer; +} + +static void +destroy_buffer(struct buffer *buffer) +{ + if (buffer->buffer) + wl_buffer_destroy(buffer->buffer); + + munmap(buffer->shm_data, buffer->size); + wl_list_remove(&buffer->buffer_link); + free(buffer); +} + +static struct buffer * +pick_free_buffer(struct window *window) +{ + struct buffer *b; + struct buffer *buffer = NULL; + + wl_list_for_each(b, &window->buffer_list, buffer_link) { + if (!b->busy) { + buffer = b; + break; + } + } + + return buffer; +} + +static void +prune_old_released_buffers(struct window *window) +{ + struct buffer *b, *b_next; + + wl_list_for_each_safe(b, b_next, + &window->buffer_list, buffer_link) { + if (!b->busy && (b->width != window->width || + b->height != window->height)) + destroy_buffer(b); + } +} + static void paint_pixels(void *image, int padding, int width, int height, uint32_t time) { @@ -144,7 +212,12 @@ create_shm_buffer(struct display *display, struct buffer *buffer, close(fd); buffer->shm_data = data; + buffer->size = size; + buffer->width = width; + buffer->height = height; + fprintf(stdout, "Created shm buffer with width %d, height %d\n", width, height); + return 0; } @@ -154,26 +227,19 @@ get_next_buffer(struct window *window) struct buffer *buffer = NULL; int ret = 0; - /* we need to create new buffers for the resized value so discard - * the 'old' one and force creation of the buffer with the newer - * dimensions */ - if (window->wait_for_configure && window->maximized) { - /* The 'old' buffer might not exist if maximized is received - * from the start. */ - if (window->buffers[0].buffer && !window->buffers[0].busy) { - wl_buffer_destroy(window->buffers[0].buffer); - window->buffers[0].buffer = NULL; - window->wait_for_configure = false; - } + if (window->needs_update_buffer) { + int i; + + for (i = 0; i < MAX_BUFFER_ALLOC; i++) + alloc_buffer(window, window->width, window->height); + + window->needs_update_buffer = false; } - if (!window->buffers[0].busy) { - buffer = &window->buffers[0]; - } else if (!window->buffers[1].busy) { - buffer = &window->buffers[1]; - } else { + buffer = pick_free_buffer(window); + if (!buffer) return NULL; - } + if (!buffer->buffer) { ret = create_shm_buffer(window->display, buffer, window->width, @@ -197,31 +263,33 @@ static const struct wl_callback_listener frame_listener = { static void redraw(void *data, struct wl_callback *callback, uint32_t time) { - struct window *window = static_cast(data); - struct buffer *buffer; + struct window *window = static_cast(data); + struct buffer *buffer; + + prune_old_released_buffers(window); - buffer = get_next_buffer(window); - if (!buffer) { - fprintf(stderr, - !callback ? "Failed to create the first buffer.\n" : - "Both buffers busy at redraw(). Server bug?\n"); - abort(); - } + buffer = get_next_buffer(window); + if (!buffer) { + fprintf(stderr, + !callback ? "Failed to create the first buffer.\n" : + "Both buffers busy at redraw(). Server bug?\n"); + abort(); + } // do the actual painting paint_pixels(buffer->shm_data, 0x0, window->width, window->height, time); - wl_surface_attach(window->surface, buffer->buffer, 0, 0); - wl_surface_damage(window->surface, 0, 0, window->width, window->height); + wl_surface_attach(window->surface, buffer->buffer, 0, 0); + wl_surface_damage(window->surface, 0, 0, window->width, window->height); - if (callback) - wl_callback_destroy(callback); + if (callback) + wl_callback_destroy(callback); - window->callback = wl_surface_frame(window->surface); - wl_callback_add_listener(window->callback, &frame_listener, window); - wl_surface_commit(window->surface); + window->callback = wl_surface_frame(window->surface); + wl_callback_add_listener(window->callback, &frame_listener, window); + wl_surface_commit(window->surface); - buffer->busy = 1; + buffer->busy = 1; } static void @@ -301,32 +369,6 @@ static const struct wl_output_listener output_listener = { display_handle_scale }; -static void -application_id(void *data, struct agl_shell_desktop *agl_shell_desktop, - const char *app_id) -{ - (void) data; - (void) agl_shell_desktop; - (void) app_id; -} - -static void -application_id_state(void *data, struct agl_shell_desktop *agl_shell_desktop, - const char *app_id, const char *app_data, - uint32_t app_state, uint32_t app_role) -{ - (void) data; - (void) app_data; - (void) agl_shell_desktop; - (void) app_id; - (void) app_state; - (void) app_role; -} - -static const struct agl_shell_desktop_listener agl_shell_desktop_listener = { - application_id, - application_id_state, -}; static void registry_handle_global(void *data, struct wl_registry *registry, uint32_t id, @@ -346,12 +388,6 @@ registry_handle_global(void *data, struct wl_registry *registry, uint32_t id, d->shm = static_cast(wl_registry_bind(registry, id, &wl_shm_interface, 1)); wl_shm_add_listener(d->shm, &shm_listener, d); - } else if (strcmp(interface, "agl_shell_desktop") == 0) { - d->agl_shell_desktop = static_cast(wl_registry_bind(registry, id, - &agl_shell_desktop_interface, 1)); - /* as an example, show how to register for events from the compositor */ - agl_shell_desktop_add_listener(d->agl_shell_desktop, - &agl_shell_desktop_listener, d); } else if (strcmp(interface, "wl_output") == 0) { d->wl_output = static_cast(wl_registry_bind(registry, id, &wl_output_interface, 1)); @@ -401,12 +437,11 @@ bus_sync_handler(GstBus *bus, GstMessage *message, gpointer user_data) struct receiver_data *d = static_cast(user_data); - if (gst_is_wayland_display_handle_need_context_message(message)) { + if (gst_is_wl_display_handle_need_context_message(message)) { GstContext *context; struct wl_display *display_handle = d->window->display->wl_display; - context = gst_wayland_display_handle_context_new(display_handle); - d->wl_video = GST_WAYLAND_VIDEO(GST_MESSAGE_SRC(message)); + context = gst_wl_display_handle_context_new(display_handle); gst_element_set_context(GST_ELEMENT(GST_MESSAGE_SRC(message)), context); goto drop; @@ -432,6 +467,19 @@ bus_sync_handler(GstBus *bus, GstMessage *message, gpointer user_data) goto drop; } + else if (GST_MESSAGE_TYPE(message) == GST_MESSAGE_ERROR) { + GError* err = NULL; + gchar* dbg_info = NULL; + + gst_message_parse_error(message, &err, &dbg_info); + g_printerr("ERROR from element %s: %s code %d\n", + GST_OBJECT_NAME(message->src), err->message, err->code); + g_printerr("Debugging info: %s\n", (dbg_info) ? dbg_info : "none"); + gst_pipeline_failed = TRUE; + g_error_free(err); + g_free(dbg_info); + goto drop; + } return GST_BUS_PASS; @@ -449,6 +497,7 @@ handle_xdg_surface_configure(void *data, struct xdg_surface *surface, uint32_t s if (window->wait_for_configure) { redraw(window, NULL, 0); + window->wait_for_configure = false; } } @@ -469,7 +518,7 @@ handle_xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel, // use our own macro as C++ can't typecast from (void *) directly WL_ARRAY_FOR_EACH(p, states, uint32_t *) { - uint32_t state = *p; + uint32_t state = *p; switch (state) { case XDG_TOPLEVEL_STATE_FULLSCREEN: window->fullscreen = 1; @@ -482,29 +531,18 @@ handle_xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel, if (width > 0 && height > 0) { if (!window->fullscreen && !window->maximized) { - window->width = width; - window->height = height; + window->init_width = width; + window->init_height = height; } window->width = width; window->height = height; } else if (!window->fullscreen && !window->maximized) { - if (width == 0) - window->width = WINDOW_WIDTH_SIZE; - else - window->width = width; - - if (height == 0) - window->height = WINDOW_HEIGHT_SIZE; - else - window->height = height; + window->width = window->init_width; + window->height = window->init_height; } - /* if we've been resized set wait_for_configure to adjust the fb size - * in the frame callback handler, which will also clear this up */ - if ((window->width > 0 && window->width != WINDOW_WIDTH_SIZE) && - (window->height > 0 && window->height != WINDOW_HEIGHT_SIZE)) { - window->wait_for_configure = true; - } + window->needs_update_buffer = true; + } static void @@ -522,6 +560,7 @@ static struct window * create_window(struct display *display, int width, int height, const char *app_id) { struct window *window; + int i; assert(display->wm_base != NULL); @@ -529,10 +568,13 @@ create_window(struct display *display, int width, int height, const char *app_id if (!window) return NULL; + wl_list_init(&window->buffer_list); window->callback = NULL; window->display = display; window->width = width; window->height = height; + window->init_width = width; + window->init_height = height; window->surface = wl_compositor_create_surface(display->wl_compositor); if (display->wm_base) { @@ -554,6 +596,9 @@ create_window(struct display *display, int width, int height, const char *app_id window->wait_for_configure = true; } + for (i = 0; i < MAX_BUFFER_ALLOC; i++) + alloc_buffer(window, window->width, window->height); + return window; } @@ -561,13 +606,14 @@ create_window(struct display *display, int width, int height, const char *app_id static void destroy_window(struct window *window) { + struct buffer *buffer, *buffer_next; + if (window->callback) wl_callback_destroy(window->callback); - if (window->buffers[0].buffer) - wl_buffer_destroy(window->buffers[0].buffer); - if (window->buffers[1].buffer) - wl_buffer_destroy(window->buffers[1].buffer); + wl_list_for_each_safe(buffer, buffer_next, + &window->buffer_list, buffer_link) + destroy_buffer(buffer); if (window->xdg_toplevel) xdg_toplevel_destroy(window->xdg_toplevel); @@ -609,11 +655,6 @@ create_display(int argc, char *argv[]) return NULL; } - if (display->agl_shell_desktop == NULL) { - fprintf(stderr, "No agl_shell extension present\n"); - return NULL; - } - wl_display_roundtrip(display->wl_display); if (!display->has_xrgb) { @@ -633,9 +674,6 @@ destroy_display(struct display *display) if (display->wm_base) xdg_wm_base_destroy(display->wm_base); - if (display->agl_shell_desktop) - agl_shell_desktop_destroy(display->agl_shell_desktop); - if (display->wl_compositor) wl_compositor_destroy(display->wl_compositor); @@ -645,16 +683,88 @@ destroy_display(struct display *display) free(display); } -int main(int argc, char *argv[]) +// stringify the un-quoted string to quoted C string +#define xstr(a) str(a) +#define str(a) #a + +GstElement* create_pipeline(int* argc, char** argv[]) +{ + GError *error = NULL; + const char *camera_device = NULL; + const char *width_str = NULL; + const char *height_str = NULL; + int width; + int height; + + // pipewire is default. + char *v4l2_path = getenv("ENABLE_V4L2_PATH"); + bool v4l2 = false; + + char pipeline_str[1024]; + + camera_device = getenv("DEFAULT_V4L2_DEVICE"); + if (!camera_device) + camera_device = get_first_camera_device(); + + width_str = getenv("DEFAULT_DEVICE_WIDTH"); + if (!width_str) + width = WINDOW_WIDTH_SIZE; + else + width = atoi(width_str); + + height_str = getenv("DEFAULT_DEVICE_HEIGHT"); + if (!height_str) + height = WINDOW_HEIGHT_SIZE; + else + height = atoi(height_str); + + if (v4l2_path == NULL) + v4l2 = false; + else if (g_str_equal(v4l2_path, "yes") || g_str_equal(v4l2_path, "true")) + v4l2 = true; + + memset(pipeline_str, 0, sizeof(pipeline_str)); + + if (v4l2) + snprintf(pipeline_str, sizeof(pipeline_str), "v4l2src device=%s ! video/x-raw,width=%d,height=%d ! waylandsink", + camera_device, width, height); + else if (gst_pipeline_failed == TRUE) { + snprintf(pipeline_str, sizeof(pipeline_str), "filesrc location=%s/still-image.jpg ! decodebin ! videoconvert ! imagefreeze ! waylandsink", + xstr(APP_DATA_PATH)); + fallback_gst_pipeline_tried = TRUE; + } + else { + snprintf(pipeline_str, sizeof(pipeline_str), "pipewiresrc ! waylandsink"); + } + + fprintf(stdout, "Using pipeline: %s\n", pipeline_str); + + GstElement *pipeline = gst_parse_launch(pipeline_str, &error); + + if (error || !pipeline) { + fprintf(stderr, "gstreamer pipeline construction failed!\n"); + free(argv); + return NULL; + } + + return pipeline; +} + +int main(int argc, char* argv[]) { int ret = 0; struct sigaction sa; struct receiver_data receiver_data = {}; - struct display *display; - struct window *window; - char pipeline_str[1024]; - GError *error = NULL; - const char *app_id = "camera-gstreamer"; + struct display* display; + struct window* window; + const char* app_id = "camera-gstreamer"; + + // for starting the application from the beginning, with a diffrent + // role we need to handle that creating the main window + if (argc >= 2 && strcmp(argv[1], "float") == 0) { + GrpcClient *client = new GrpcClient(); + client->SetAppFloat(std::string(app_id), 30, 400); + } sa.sa_sigaction = signal_int; sigemptyset(&sa.sa_mask); @@ -662,44 +772,28 @@ int main(int argc, char *argv[]) sigaction(SIGINT, &sa, NULL); int gargc = 2; - char **gargv = static_cast(calloc(2, sizeof(char *))); + char** gargv = static_cast(calloc(2, sizeof(char*))); gargv[0] = strdup(argv[0]); gargv[1] = strdup("--gst-debug-level=2"); - memset(pipeline_str, 0, sizeof(pipeline_str)); - snprintf(pipeline_str, sizeof(pipeline_str), "v4l2src device=%s ! video/x-raw,width=%d,height=%d ! waylandsink", - get_first_camera_device(), WINDOW_WIDTH_SIZE, WINDOW_HEIGHT_SIZE); - gst_init(&gargc, &gargv); - setbuf(stdout, NULL); - fprintf(stdout, "Using pipeline: %s\n", pipeline_str); + gst_init(&gargc, &gargv); - GstElement *pipeline = gst_parse_launch(pipeline_str, &error); - if (error || !pipeline) { - fprintf(stderr, "gstreamer pipeline construction failed!\n"); - free(gargv); - return EXIT_FAILURE; - } + receiver_data.pipeline = create_pipeline(&gargc, &gargv); - receiver_data.pipeline = pipeline; + if (!receiver_data.pipeline) + return EXIT_FAILURE; display = create_display(argc, argv); if (!display) return -1; - // if you'd want to place the video in a pop-up/dialog type of window: - // agl_shell_desktop_set_app_property(display->agl_shell_desktop, app_id, - // AGL_SHELL_DESKTOP_APP_ROLE_POPUP, - // WINDOW_WIDTH_POS_X, WINDOW_WIDTH_POS_Y, - // 0, 0, WINDOW_WIDTH_SIZE, WINDOW_HEIGHT_SIZE, - // display->wl_output); - // we use the role to set a correspondence between the top level // surface and our application, with the previous call letting the // compositor know that we're one and the same - window = create_window(display, WINDOW_WIDTH_SIZE, WINDOW_HEIGHT_SIZE, app_id); + window = create_window(display, WINDOW_WIDTH_SIZE, WINDOW_HEIGHT_SIZE, app_id); if (!window) { free(gargv); @@ -717,25 +811,38 @@ int main(int argc, char *argv[]) redraw(window, NULL, 0); } - GstBus *bus = gst_element_get_bus(pipeline); + GstBus *bus = gst_element_get_bus(receiver_data.pipeline); gst_bus_add_signal_watch(bus); g_signal_connect(bus, "message::error", G_CALLBACK(error_cb), &receiver_data); gst_bus_set_sync_handler(bus, bus_sync_handler, &receiver_data, NULL); gst_object_unref(bus); - gst_element_set_state(pipeline, GST_STATE_PLAYING); + gst_element_set_state(receiver_data.pipeline, GST_STATE_PLAYING); fprintf(stdout, "gstreamer pipeline running\n"); // run the application - while (running && ret != -1) + while (running && ret != -1) { ret = wl_display_dispatch(display->wl_display); + if (gst_pipeline_failed && fallback_gst_pipeline_tried == FALSE) { + gst_element_set_state(receiver_data.pipeline, GST_STATE_NULL); + gst_object_unref(receiver_data.pipeline); + /* retry with fallback pipeline */ + receiver_data.pipeline = create_pipeline(&gargc, &gargv); + GstBus *bus = gst_element_get_bus(receiver_data.pipeline); + gst_bus_add_signal_watch(bus); + g_signal_connect(bus, "message::error", G_CALLBACK(error_cb), &receiver_data); + gst_bus_set_sync_handler(bus, bus_sync_handler, &receiver_data, NULL); + gst_object_unref(bus); + gst_element_set_state(receiver_data.pipeline, GST_STATE_PLAYING); + } + } + gst_element_set_state(receiver_data.pipeline, GST_STATE_NULL); + gst_object_unref(receiver_data.pipeline); destroy_window(window); destroy_display(display); free(gargv); - gst_element_set_state(pipeline, GST_STATE_NULL); - gst_object_unref(pipeline); return ret; }