Hello,
• Hardware Platform: Jetson Xavier NX
• JetPack Version: 4.6 [L4T 32.6.1]
I integrate GStreamer in GTK+. Two displays (DP(1920x1080) as main display; HDMI(1920X1080) as secondary display) are connected to the NX. When I try to move gtk+ window from DP to HDMI using gtk_window_move (GTK_WINDOW (data->main_window), 1920, 0), it dosen’t work.
Here is the whole code:
#include <string.h>
#include <gtk/gtk.h>
#include <gst/gst.h>
#include <gst/video/videooverlay.h>
#include <gdk/gdk.h>
#if defined (GDK_WINDOWING_X11)
#include <gdk/gdkx.h>
#elif defined (GDK_WINDOWING_WIN32)
#include <gdk/gdkwin32.h>
#elif defined (GDK_WINDOWING_QUARTZ)
#include <gdk/gdkquartz.h>
#endif
/* Structure to contain all our information, so we can pass it around */
typedef struct _CustomData {
GstElement *playbin; /* Our one and only pipeline */
GtkWidget *main_window; /* The uppermost window, containing all other windows */
GtkWidget *video_window; /* The drawing area where the video will be shown */
GstState state; /* Current state of the pipeline */
gint64 duration; /* Duration of the clip, in nanoseconds */
} CustomData;
/* This function is called periodically to refresh the GUI */
static gboolean refresh_ui (CustomData *data) {
static gboolean flag = FALSE;
static gint count = FALSE;
if (flag) {
gtk_window_resize (GTK_WINDOW (data->main_window), 1920, 1080);
gtk_window_move (GTK_WINDOW (data->main_window), 0, 0);
} else {
gtk_window_resize (GTK_WINDOW (data->main_window), 1920, 1080);
gtk_window_move (GTK_WINDOW (data->main_window), 1920, 0);
}
flag = !flag;
if (count++ > 20) {
return FALSE;
}
return TRUE;
}
/* This function is called when the GUI toolkit creates the physical window that will hold the video.
* At this point we can retrieve its handler (which has a different meaning depending on the windowing system)
* and pass it to GStreamer through the VideoOverlay interface. */
static void realize_cb (GtkWidget *widget, CustomData *data) {
GdkWindow *window = gtk_widget_get_window (widget);
if (!gdk_window_ensure_native (window))
g_error ("Couldn't create native window needed for GstVideoOverlay!");
}
/* This function is called when the STOP button is clicked */
static void stop_cb (GtkButton *button, CustomData *data) {
gst_element_set_state (data->playbin, GST_STATE_READY);
}
/* This function is called when the main window is closed */
static void delete_event_cb (GtkWidget *widget, GdkEvent *event, CustomData *data) {
stop_cb (NULL, data);
gtk_main_quit ();
}
/* This function is called everytime the video window needs to be redrawn (due to damage/exposure,
* rescaling, etc). GStreamer takes care of this in the PAUSED and PLAYING states, otherwise,
* we simply draw a black rectangle to avoid garbage showing up. */
static gboolean draw_cb (GtkWidget *widget, cairo_t *cr, CustomData *data) {
g_print("Enter draw_cb.\n");
static gboolean flag = FALSE;
if (data->state == GST_STATE_PAUSED) {
if (!flag) {
g_print("Skip draw background.\n");
flag = !flag;
return FALSE;
}
GtkAllocation allocation;
/* Cairo is a 2D graphics library which we use here to clean the video window.
* It is used by GStreamer for other reasons, so it will always be available to us. */
gtk_widget_get_allocation (widget, &allocation);
cairo_set_source_rgb (cr, 0, 0, 0);
cairo_rectangle (cr, 0, 0, allocation.width, allocation.height);
cairo_fill (cr);
GdkWindow *window = gtk_widget_get_window (widget);
guintptr window_handle;
window_handle = GDK_WINDOW_XID (window);
g_print("The background of window handle with window id 0x%lx.\n", window_handle);
GstElement *render = gst_bin_get_by_name (GST_BIN(data->playbin), "render");
gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (render), window_handle);
g_print("Video set window handle with window id 0x%lx.\n", window_handle);
gst_object_unref (render);
}
return FALSE;
}
/* This creates all the GTK+ widgets that compose our application, and registers the callbacks */
static void create_ui (CustomData *data) {
data->main_window = gtk_window_new (GTK_WINDOW_POPUP);
g_signal_connect (G_OBJECT (data->main_window), "delete-event", G_CALLBACK (delete_event_cb), data);
data->video_window = gtk_drawing_area_new ();
g_signal_connect (data->video_window, "realize", G_CALLBACK (realize_cb), data);
g_signal_connect (data->video_window, "draw", G_CALLBACK (draw_cb), data);
gtk_container_add(GTK_CONTAINER(data->main_window), data->video_window);
gtk_window_set_default_size(GTK_WINDOW(data->main_window), 1920, 1080);
gtk_window_move(GTK_WINDOW (data->main_window), 0, 0);
gtk_widget_show_all (data->main_window);
}
/* This function is called when an error message is posted on the bus */
static void error_cb (GstBus *bus, GstMessage *msg, CustomData *data) {
GError *err;
gchar *debug_info;
/* Print error details on the screen */
gst_message_parse_error (msg, &err, &debug_info);
g_printerr ("Error received from element %s: %s\n", GST_OBJECT_NAME (msg->src), err->message);
g_printerr ("Debugging information: %s\n", debug_info ? debug_info : "none");
g_clear_error (&err);
g_free (debug_info);
/* Set the pipeline to READY (which stops playback) */
gst_element_set_state (data->playbin, GST_STATE_READY);
}
/* This function is called when an End-Of-Stream message is posted on the bus.
* We just set the pipeline to READY (which stops playback) */
static void eos_cb (GstBus *bus, GstMessage *msg, CustomData *data) {
g_print ("End-Of-Stream reached.\n");
gst_element_set_state (data->playbin, GST_STATE_READY);
}
/* This function is called when the pipeline changes states. We use it to
* keep track of the current state. */
static void state_changed_cb (GstBus *bus, GstMessage *msg, CustomData *data) {
GstState old_state, new_state, pending_state;
gst_message_parse_state_changed (msg, &old_state, &new_state, &pending_state);
if (GST_MESSAGE_SRC (msg) == GST_OBJECT (data->playbin)) {
data->state = new_state;
g_print ("State set to %s\n", gst_element_state_get_name (new_state));
}
}
int main(int argc, char *argv[]) {
CustomData data;
GstStateChangeReturn ret;
GstBus *bus;
/* Initialize GTK */
gtk_init (&argc, &argv);
/* Initialize GStreamer */
gst_init (&argc, &argv);
/* Initialize our data structure */
memset (&data, 0, sizeof (data));
data.duration = GST_CLOCK_TIME_NONE;
/* Create the elements */
data.playbin = gst_parse_launch ("videotestsrc is-live=true ! video/x-raw,format=NV12,width=1920,height=1080,framerate=30/1 "
"! videoconvert ! xvimagesink name=render sync=false", NULL);
if (!data.playbin) {
g_printerr ("Not all elements could be created.\n");
return -1;
}
/* Create the GUI */
create_ui (&data);
/* Instruct the bus to emit signals for each received message, and connect to the interesting signals */
bus = gst_element_get_bus (data.playbin);
gst_bus_add_signal_watch (bus);
g_signal_connect (G_OBJECT (bus), "message::error", (GCallback)error_cb, &data);
g_signal_connect (G_OBJECT (bus), "message::eos", (GCallback)eos_cb, &data);
g_signal_connect (G_OBJECT (bus), "message::state-changed", (GCallback)state_changed_cb, &data);
gst_object_unref (bus);
/* Start playing */
ret = gst_element_set_state (data.playbin, GST_STATE_PLAYING);
if (ret == GST_STATE_CHANGE_FAILURE) {
g_printerr ("Unable to set the pipeline to the playing state.\n");
gst_object_unref (data.playbin);
return -1;
}
/* Register a function that GLib will call every second */
g_timeout_add_seconds (3, (GSourceFunc)refresh_ui, &data);
/* Start the GTK main loop. We will not regain control until gtk_main_quit is called. */
gtk_main ();
/* Free resources */
gst_element_set_state (data.playbin, GST_STATE_NULL);
gst_object_unref (data.playbin);
return 0;
}
Compile with the command
gcc gtk_test.c -o gtk_test `pkg-config --cflags --libs gstreamer-video-1.0 gtk±3.0 gstreamer-1.0`
Howerver, when I try to change 1920 → 1921, using gtk_window_move (GTK_WINDOW (data->main_window), 1921, 0), move to the hdmi display successfully. It seems a bug about the position point 1920. Any help would be appreciated.
Thank you