Как передать веб-камеру в окно GTK?

Мне нужно получить видеопоток с веб-камеры, используя C, GTK2/3, Cairo. Однако не могу найти никаких упоминаний об этом.

Ниже показано, как я пытался использовать opencv. Это безуспешно. Мне нужно знать какой-то другой метод, используя gstreamer, cairo.

Я ожидаю, что простой код получит видео в окно GTK. Очень признателен, если кто-то может предоставить пример кода.

    /*
gcc -o weby3 att3.c `pkg-config --libs --cflags gtk+-2.0 opencv`
*/

#include "highgui.h"
#include <gtk/gtk.h>
#include <opencv/cv.h>
#include <opencv/cxcore.h>

GdkPixbuf* pix;
IplImage* frame;
CvCapture* capture;

gboolean
expose_event_callback(GtkWidget *widget, GdkEventExpose *event, gpointer data)
{
  while(1) {
         frame = cvQueryFrame( capture );
          if(!frame) break;  

         pix = gdk_pixbuf_new_from_data((guchar*) frame->imageData,
         GDK_COLORSPACE_RGB, FALSE, frame->depth, frame->width,
         frame->height, (frame->widthStep), NULL, NULL);


         gdk_draw_pixbuf(widget->window,
   widget->style->fg_gc[GTK_WIDGET_STATE (widget)], pix, 0, 0, 0, 0,
   -1, -1, GDK_RGB_DITHER_NONE, 0, 0); /* Other possible values are  GDK_RGB_DITHER_MAX,  GDK_RGB_DITHER_NORMAL */

         char c = cvWaitKey(33);
         if( c == 27 ) break;
   }

   cvReleaseCapture( &capture );   
   return TRUE;
}

int main( int argc, char** argv ) {

   /* GtkWidget is the storage type for widgets */
   GtkWidget *window;
   GtkWidget *drawing_area;

   gtk_init (&argc, &argv);

   CvCapture* capture = cvCreateCameraCapture(0); 
   /* create a new window */
   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
   gtk_window_set_title (GTK_WINDOW (window), "Hello WebCam and OpenCV!");
   g_signal_connect (G_OBJECT (window), "destroy",
    G_CALLBACK (gtk_main_quit), NULL);

   /* Sets the border width of the window. */
   gtk_container_set_border_width (GTK_CONTAINER (window), 10);

   /* Now add a child widget to the aspect frame */
   drawing_area = gtk_drawing_area_new();


   /* window since we are forcing a aspect ratio */
   gtk_widget_set_size_request(drawing_area, 600, 400);
   gtk_container_add(GTK_CONTAINER (window), drawing_area);
   gtk_widget_show(drawing_area);

   g_signal_connect (G_OBJECT (drawing_area), "expose_event",
      G_CALLBACK (expose_event_callback), NULL);

   /* and the window */
   gtk_widget_show (window);

   /* All GTK applications must have a gtk_main(). Control ends here
    * and waits for an event to occur (like a key press or
    * mouse event). */
   gtk_main ();

   return 0;
}

person inckka    schedule 18.02.2016    source источник
comment
Возможно, вы захотите проверить библиотеку сыра, которая напрямую интегрируется с Gtk.   -  person TingPing    schedule 19.02.2016
comment
@TingPing Я уже обеспокоен. Проблема в том, что нет достаточного количества учебных материалов для начала.   -  person inckka    schedule 19.02.2016


Ответы (1)


Основная проблема, которую я здесь вижу, заключается в том, что вы неправильно интегрируете свой код opencv в модель событий GTK.

GTK в основном работает с насосом сообщений. Сообщения отправляются в очередь, и GTK считывает их и удаляет из очереди, чтобы отреагировать на эти сообщения. В GTK 2 событие expose-event генерируется, когда необходимо отрисовать виджет или его часть (в GTK 3 используется событие draw). Вы должны подключить обратный вызов к этому событию, чтобы поймать событие. Затем в обратном вызове вы рисуете один кадр, а затем возвращаете управление GTK. Он снова вызовет ваш обратный вызов, когда ему понадобится следующий кадр.

Проблема здесь в том, что вы никогда не возвращаете руку GTK, так как ваш код opencv заключен в бесконечный цикл while(1). Вместо этого вы должны просто нарисовать один кадр и вернуться.

Логика ключевых событий также должна обрабатываться с помощью GTK, а не в обратном вызове expose-event. Просто позвоните gtk_main_quit, как только вы увидите нажатие клавиши, которое вы ждете.

ИЗМЕНИТЬ:

Как только вы можете отобразить один кадр, вы должны отобразить их все. Вызовите gtk_widget_queue_draw, чтобы сообщить GTK, что виджет нужно перерисовать.

Как только это будет сделано, вы увидите, что вам нужно периодически вызывать gtk_widget_queue_draw. Это легко сделать с помощью функций GLib, таких как g_timeout_add или g_idle_add. Эти функции сообщают основному циклу обработки событий (то, что я назвал выше «насосом сообщений»), вызывать обратный вызов через определенное время (для g_timeout_add) или только тогда, когда нет других событий для обработки (для g_idle_add). этот обратный вызов, и все готово.

Имейте в виду, что g_timeout_add не позволяет получить идеальное время, поэтому время между каждым кадром может сильно отличаться от одного кадра к другому. Вы можете избежать этого, увеличив приоритет, переданный g_timeout_add. Вы также можете использовать GTimer, чтобы вычислить в обратном вызове тайм-аута время, прошедшее между последним кадром и текущим временем, и вывести время, оставшееся до следующего кадра. Затем вызовите g_timeout_add с более точным временем. Не забудьте вернуть FALSE вместо TRUE в конце обратного вызова с тайм-аутом, если вы используете трюк GTimer, иначе у вас будут обратные вызовы с тайм-аутом n, работающие в кадре n.

person liberforce    schedule 18.02.2016
comment
Ты прав. Теперь я могу отобразить один кадр. Но как мы можем получить непрерывные кадры без цикла? - person inckka; 19.02.2016
comment
Просто добавил больше информации об этом - person liberforce; 19.02.2016
comment
Для этого конкретного сценария добавление gtk_widget_queue_draw и g_timeout_add немного странно. можете ли вы объяснить это, используя быстрый пример, пожалуйста? - person inckka; 22.02.2016
comment
Эй, я понял твой способ, и теперь он работает нормально. - person inckka; 23.02.2016