#include <fcntl.h>
#include <assert.h>
-#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 <gst/gst.h>
#include <gst/video/videooverlay.h>
#include <gst/wayland/wayland.h>
+#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
} output_data;
struct xdg_wm_base *wm_base;
- struct agl_shell_desktop *agl_shell_desktop;
-
int has_xrgb;
};
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);
buffer->height = height;
fprintf(stdout, "Created shm buffer with width %d, height %d\n", width, height);
+
return 0;
}
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,
d->shm = static_cast<struct wl_shm *>(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<struct agl_shell_desktop *>(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<struct wl_output *>(wl_registry_bind(registry, id,
&wl_output_interface, 1));
struct receiver_data *d =
static_cast<struct receiver_data *>(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;
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;
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) {
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) {
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);
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[])
{
- int ret = 0;
- struct sigaction sa;
- struct receiver_data receiver_data = {};
- struct display *display;
- struct window *window;
+ GError *error = NULL;
const char *camera_device = NULL;
const char *width_str = NULL;
const char *height_str = NULL;
bool v4l2 = false;
char pipeline_str[1024];
- GError *error = NULL;
- const char *app_id = "camera-gstreamer";
-
- sa.sa_sigaction = signal_int;
- sigemptyset(&sa.sa_mask);
- sa.sa_flags = SA_RESETHAND | SA_SIGINFO;
- sigaction(SIGINT, &sa, NULL);
-
- int gargc = 2;
- char **gargv = static_cast<char **>(calloc(2, sizeof(char *)));
-
- gargv[0] = strdup(argv[0]);
- gargv[1] = strdup("--gst-debug-level=2");
camera_device = getenv("DEFAULT_V4L2_DEVICE");
if (!camera_device)
if (v4l2)
snprintf(pipeline_str, sizeof(pipeline_str), "v4l2src device=%s ! video/x-raw,width=%d,height=%d ! waylandsink",
camera_device, width, height);
- else
- snprintf(pipeline_str, sizeof(pipeline_str), "pipewiresrc ! video/x-raw,width=%d,height=%d ! waylandsink",
- width, height);
-
- gst_init(&gargc, &gargv);
-
- setbuf(stdout, NULL);
+ 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(gargv);
- return EXIT_FAILURE;
+ free(argv);
+ return NULL;
}
- receiver_data.pipeline = pipeline;
+ 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;
+ 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);
+ sa.sa_flags = SA_RESETHAND | SA_SIGINFO;
+ sigaction(SIGINT, &sa, NULL);
+
+ int gargc = 2;
+ char** gargv = static_cast<char**>(calloc(2, sizeof(char*)));
+
+ gargv[0] = strdup(argv[0]);
+ gargv[1] = strdup("--gst-debug-level=2");
+
+ setbuf(stdout, NULL);
+
+ gst_init(&gargc, &gargv);
+
+ receiver_data.pipeline = create_pipeline(&gargc, &gargv);
+
+ 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
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);
-
- gst_element_set_state(pipeline, GST_STATE_NULL);
- gst_object_unref(pipeline);
+ 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);