Using a GSource to dispatch camera images to display in a GUI



Hi all,

I'm currently hacking on a system that can analyse images coming from a
camera (on the fly), or from a pre-recorded set of images
(pos-analysis). I want to be able to use either source transparently.
But as I don't know how much time the pre-recorded images will take to
process, I'd like to have the processing be asynchronous.

Given these requirements, I thought of using GSource, because an image
arrival would be seen like an asynchronous event source. So, I'm
currently doing a small GUI application, but I have problems displaying
the images provided by the camera source. Note that in a non-GUI
application, everything works, and I'm able to save every image on the
hard disk for post analysis.

Currently, I'm doing everything in the main loop (there's no other
thread), but the results are bad. Either the GUI freezes and the other
controls are not drawn (as if too much time was spent processing the
camera source events), or if I lower the acquisition period, I only get
3 or 4 frames incompletely drawn.

Maybe I'm not using GSource as I should, but I couldn't find much
documentation on it. Especially since I'm using it with no file
descriptor to poll. I read the glib code for timeout sources, and could
not find something I'm doing wrong. So if someone could give me some
hints on how I may be using it in a bad way, or tell me if my approach
and choice of GSource was a good one, it would be very appreciated.

Here is the code for the caera event source, and for the GUI app.

Thanks in advance.

/*****************
* camera-source.c
******************/
static GSourceFuncs
camera_source_funcs =
{
         camera_source_prepare,
         camera_source_check,
         camera_source_dispatch,
         NULL
};

GSource *
camera_source_new(CamDevice *camdev)
{
         GSource *source = g_source_new(&camera_source_funcs,
                         sizeof(CameraSource));
         CameraSource* self = CAMERASOURCE(source);
         self->device = camdev;
         self->pixbuf = NULL;
         self->frame_number = -1;

         return source;
}

inline void
camera_source_free(gpointer source)
{
         if (source != NULL)
         {
                 CameraSource *self = source;
                 if (self->pixbuf != NULL)
                         g_object_unref(self->pixbuf);
                 g_free(self);
         }
}

static gboolean
camera_source_prepare (GSource *source,
                 gint *timeout)
{
         CamDevice *camdev;
         g_assert(source != NULL);

         camdev = CAMERASOURCE(source)->device;
         *timeout = ACQUISITION_PERIOD; /* milisecondes */
         camdevice_request_image(camdev);
         return FALSE;
}

static gboolean
camera_source_check (GSource *source)
{
         CameraSource *self;
         gboolean result;
         g_assert(source != NULL);

         self = CAMERASOURCE(source);
         result = camdevice_get_pending_image(self->device, &self->pixbuf);
         /* camdevice_get_pending_image returns FALSE if no image is ready,
         as I had to workaround the implementation of the 3rd part vendor
         of the camera which only proposes a blocking, synchronous API to
         get images */
         self->frame_number++;

         return result;

}

static gboolean
camera_source_dispatch (GSource *source,
                 GSourceFunc callback,
                 gpointer user_data)
{
         g_assert(source != NULL);

         if (!callback)
         {
                 g_warning("No callback defined.\n"
                                 "Call g_source_set_callback");
                 return FALSE;
         }

         if (callback(user_data))
                 return TRUE;

         return FALSE;
}



/**********
* gt-dif.c
***********/
#include "camdevice.h"            /* for CamDevice */
#include "camera-source.h"        /* for CameraSource */


static void
on_quit (gpointer user_data)
{
         gtk_main_quit ();
}

static gboolean
on_image_received (gpointer data)
{
         GtkWidget *da = data;
         g_assert (da != NULL);
         g_debug (G_STRFUNC);
         gdk_window_invalidate_rect (da->window,
                         &da->allocation,
                         FALSE);
         return TRUE;
}

gboolean
on_drawing_area_expose_event (GtkWidget *widget,
                 GdkEventExpose *event,
                 gpointer data)
{
         CameraSource *camsrc = data;
         g_assert (camsrc != NULL);
         g_debug ("%s : expose-event", G_STRFUNC);

         if (camsrc->pixbuf == NULL)
                 return FALSE;

         gdk_draw_pixbuf (widget->window,
                         widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
                         camsrc->pixbuf,
                         0, 0, 0, 0,
                         -1, -1,
                         GDK_RGB_DITHER_NONE,
                         0, 0);
         return TRUE;
}

static GPtrArray*
recorder_autodetect_sources (void)
{
         CamDevice *camdev;
         GPtrArray *sources = NULL;

         /* Discover every camera connected */
         while ((camdev = camdevice_new ()) != NULL)
         {
                 GSource *source = camera_source_new (camdev);
                 g_source_attach (source, NULL);

                 if (G_UNLIKELY (sources == NULL))
                         sources = g_ptr_array_new ();

                 g_ptr_array_add (sources, source);
                 g_print ("Camera '%s' was detected\n",
                                 camdevice_get_serial (camdev));
         }

         return sources;
}

static GtkWidget *
make_gui (const GPtrArray *camsources)
{
         GtkWidget *main_window;
         GtkWidget *box;

         main_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
         gtk_window_set_title (GTK_WINDOW (main_window), "gtk-dif");
         gtk_window_set_default_size (GTK_WINDOW (main_window), 320, 240);

         box = gtk_vbox_new (FALSE, 10);
         gtk_container_add (GTK_CONTAINER (main_window), box);

         if (camsources != NULL)
         {
                 gint i = 0;
                 for (i = 0; i < camsources->len; i++)
                 {
                         GtkWidget *label;
                         GtkWidget *da;
                         GSource *camsource;

                         camsource = g_ptr_array_index (camsources, i);
                         g_debug ("Adding camera '%s'",
camdevice_get_serial (CAMERASOURCE
(camsource)->device));

                         /* Label avec le nom de la caméra */
                         label = gtk_label_new (camdevice_get_serial
(CAMERASOURCE
(camsource)->device));
                         gtk_box_pack_start (GTK_BOX (box), label, FALSE,
FALSE, 0);

                         /* Image de la caméra */
                         da = gtk_drawing_area_new ();
                         gtk_widget_set_size_request (da, 320, 240);
                         gtk_box_pack_start (GTK_BOX (box), da, FALSE,
FALSE, 0);

                         /* Signaux */
                         g_signal_connect (da, "expose-event",
                                         G_CALLBACK
(on_drawing_area_expose_event),
                                         camsource);
                         g_source_set_callback (camsource,
on_image_received, da, NULL);
                 }
         }

         gtk_widget_show_all (main_window);
         g_signal_connect (main_window, "destroy", G_CALLBACK (on_quit),
NULL);
         return main_window;
}

int main (int argc, char *argv[])
{
         GPtrArray *camera_sources = NULL;
         GtkWidget *main_window;

         /* Locale management */
         setlocale (LC_ALL, "");

         g_type_init ();
         gtk_init (&argc, &argv);

         /* Image sources creation */
         camera_sources = recorder_autodetect_sources ();
         if (camera_sources == NULL)
                 g_debug ("No camera détected");
         main_window = make_gui (camera_sources);
         gtk_main ();

         /* FIXME: Leaking camera sources */

         return EXIT_SUCCESS;
}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]