$ git clone http://tcclient.ion.nu/tc_client.git
commit da5cc62be0d5a4a2e789e0178e7a5c9ac5345cce
Author: Alicia <...>
Date:   Sun Sep 25 15:14:08 2016 +0200

    tc_client-gtk: changed camera input for broadcasting from using a thread to using g_timeout.

diff --git a/ChangeLog b/ChangeLog
index 3ade7d4..4cd4e95 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -4,6 +4,7 @@ tc_client-gtk: merged camera_remove() and camera_removebynick() into a single fu
 tc_client-gtk: moved the postprocessing code into its own source file.
 tc_client-gtk: added greenscreen postprocessor.
 tc_client-gtk: added GTK+2 compatibility code related to the greenscreen postprocessor.
+tc_client-gtk: changed camera input for broadcasting from using a thread to using g_timeout.
 tc_client-gtk and camviewer: updated to libavcodec's avcodec_{send,receive}_{frame,packet} API.
 0.39:
 Added a /closecam command to stop receiving a cam stream.
diff --git a/Makefile b/Makefile
index ac68cc2..d4c5f0e 100644
--- a/Makefile
+++ b/Makefile
@@ -52,7 +52,7 @@ ifdef SWSCALE_LIBS
     else
       CONFINFO+=|  escapi windows camera support will not be enabled
     endif
-    windowstargets: camviewer tc_client-gtk tc_client-gtk-camthread
+    windowstargets: camviewer tc_client-gtk
  @echo
  @echo 'To build the core (tc_client.exe), enter this directory from cygwin (or MSYS2 non-MinGW shell) and type make'
   endif
@@ -118,30 +118,12 @@ tc_client-gtk: $(TC_CLIENT_GTK_OBJ) camplaceholder.gif
 camplaceholder.gif: utilities/gtk/gencamplaceholder.sh utilities/gtk/camplaceholder.xcf utilities/gtk/spinnerdot.xcf
  utilities/gtk/gencamplaceholder.sh
 
-# Workaround for windows' lack of fork() and inflexibility of having or not having a terminal
-utilities/gtk/camthread.gen.c: utilities/gtk/media.c
- sed -n -e '/^ *#/p' utilities/gtk/media.c > $@
- echo 'int main(int argc, char** argv){' >> $@
- echo 'avcodec_register_all();' >> $@
- echo 'AVCodec* vencoder=avcodec_find_encoder(AV_CODEC_ID_FLV1);' >> $@
- echo 'cam_img_filepicker=camselect_file;' >> $@
- echo 'gtk_init(&argc, &argv);' >> $@
- echo 'int campipe[]={0,1};' >> $@
- echo 'setmode(1, O_BINARY);' >> $@
- echo 'CAM* cam=cam_open(argv[1]);' >> $@
- echo 'unsigned int delay=atoi(argv[2]);' >> $@
- echo 'struct size camsize_out={.width=320, .height=240};' >> $@
- sed -n -e '/if(!camproc)$$/,/^  }/p' utilities/gtk/media.c | sed -e '1,3d' >> $@
- sed -n -e '/ camselect_file/,/^}/p' utilities/gtk/media.c >> $@
-tc_client-gtk-camthread: utilities/gtk/camthread.gen.o utilities/compat.o libcamera.a
- $(CC) $^ $(LIBS) $(GTK_LIBS) $(AVCODEC_LIBS) $(AVUTIL_LIBS) $(SWSCALE_LIBS) $(AVRESAMPLE_LIBS) -o $@
-
 libcamera.a: $(LIBCAMERA_OBJ)
  $(AR) cru $@ $^
  $(RANLIB) $@
 
 clean:
- rm -f $(OBJ) $(IRCHACK_OBJ) $(MODBOT_OBJ) $(CAMVIEWER_OBJ) $(CURSEDCHAT_OBJ) $(TC_CLIENT_GTK_OBJ) $(LIBCAMERA_OBJ) tc_client irchack modbot camviewer cursedchat tc_client-gtk camplaceholder.gif utilities/gtk/camthread.gen.c utilities/gtk/camthread.gen.o tc_client-gtk-camthread
+ rm -f $(OBJ) $(IRCHACK_OBJ) $(MODBOT_OBJ) $(CAMVIEWER_OBJ) $(CURSEDCHAT_OBJ) $(TC_CLIENT_GTK_OBJ) $(LIBCAMERA_OBJ) tc_client irchack modbot camviewer cursedchat tc_client-gtk camplaceholder.gif
 
 SOURCES=Makefile client.c amfparser.c rtmp.c numlist.c amfwriter.c idlist.c colors.c endian.c media.c amfparser.h rtmp.h numlist.h amfwriter.h idlist.h colors.h endian.h media.h LICENSE README ChangeLog crossbuild.sh testbuilds.sh configure
 SOURCES+=utilities/irchack/irchack.c
diff --git a/utilities/gtk/camviewer.c b/utilities/gtk/camviewer.c
index daa6ab1..c9c2a94 100644
--- a/utilities/gtk/camviewer.c
+++ b/utilities/gtk/camviewer.c
@@ -522,7 +522,6 @@ gboolean handledata(GIOChannel* iochannel, GIOCondition condition, gpointer data
     idend[0]=0;
     camera_remove(nick, 1); // Remove any duplicates
     struct camera* cam=camera_new(nick, id);
-    cam->placeholder=g_timeout_add(100, camplaceholder_update, cam->id);
     cam->vctx=avcodec_alloc_context3(data->vdecoder);
     avcodec_open2(cam->vctx, data->vdecoder, 0);
 #if defined(HAVE_AVRESAMPLE) || defined(HAVE_SWRESAMPLE)
@@ -568,11 +567,10 @@ gboolean handledata(GIOChannel* iochannel, GIOCondition condition, gpointer data
   if(!strcmp(buf, "Server disconnected"))
   {
     printchat(buf, 0);
-    if(camproc)
+    if(camout_cam)
     {
-      g_source_remove(cameventsource);
-      kill(camproc, SIGINT);
-      camproc=0;
+      cam_close(camout_cam);
+      camout_cam=0;
     }
     return 1;
   }
@@ -603,27 +601,27 @@ void audiothread(int fd)
 }
 #endif
 
+void togglecam_cancel(void)
+{
+  GtkCheckMenuItem* item=GTK_CHECK_MENU_ITEM(gtk_builder_get_object(gui, "menuitem_broadcast_camera"));
+  gtk_check_menu_item_set_active(item, 0);
+}
+
 void togglecam(GtkCheckMenuItem* item, struct viddata* data)
 {
   if(!gtk_check_menu_item_get_active(item))
   {
-    g_source_remove(cameventsource);
-    kill(camproc, SIGINT);
-    camproc=0;
+    if(!camout_cam){return;}
+    cam_close(camout_cam);
+    camout_cam=0;
     if(camera_find("out"))
     {
       dprintf(tc_client_in[1], "/camdown\n");
-      dprintf(tc_client[1], "VideoEnd: out\n"); // Close our local display
+      camera_remove("out", 0); // Close our local display
     }
     return;
   }
-  GtkWidget* window=GTK_WIDGET(gtk_builder_get_object(gui, "camselection"));
-  gtk_widget_show_all(window);
-
-  // Start a cam thread for the selected cam
-  GtkComboBox* combo=GTK_COMBO_BOX(gtk_builder_get_object(gui, "camselect_combo"));
-  GIOChannel* channel=camthread(gtk_combo_box_get_active_id(combo), data->vencoder, 100000);
-  cameventsource=g_io_add_watch(channel, G_IO_IN, cam_encode, 0);
+  camselect_open(startcamout, togglecam_cancel);
 }
 
 gboolean handleresize(GtkWidget* widget, GdkEventConfigure* event, struct viddata* data)
@@ -957,7 +955,7 @@ int main(int argc, char** argv)
   campreview.frame=av_frame_alloc();
   campreview.frame->data[0]=0;
   GtkComboBox* combo=GTK_COMBO_BOX(gtk_builder_get_object(gui, "camselect_combo"));
-  g_signal_connect(combo, "changed", G_CALLBACK(camselect_change), data->vencoder);
+  g_signal_connect(combo, "changed", G_CALLBACK(camselect_change), 0);
   // Signals for cancelling
   item=GTK_WIDGET(gtk_builder_get_object(gui, "camselection"));
   g_signal_connect(item, "delete-event", G_CALLBACK(camselect_cancel), 0);
@@ -965,7 +963,7 @@ int main(int argc, char** argv)
   g_signal_connect(item, "clicked", G_CALLBACK(camselect_cancel), 0);
   // Signals for switching from preview to streaming
   item=GTK_WIDGET(gtk_builder_get_object(gui, "camselect_ok"));
-  g_signal_connect(item, "clicked", G_CALLBACK(camselect_accept), data->vencoder);
+  g_signal_connect(item, "clicked", G_CALLBACK(camselect_accept), 0);
   // Enable the "img" camera
   cam_img_filepicker=camselect_file;
   // Populate list of cams
diff --git a/utilities/gtk/media.c b/utilities/gtk/media.c
index 78b2f27..427e833 100644
--- a/utilities/gtk/media.c
+++ b/utilities/gtk/media.c
@@ -18,9 +18,6 @@
 #include <string.h>
 #include <gtk/gtk.h>
 #include <libavcodec/avcodec.h>
-#ifndef _WIN32
-#include <sys/prctl.h>
-#endif
 #include <libswscale/swscale.h>
 #if LIBAVUTIL_VERSION_MAJOR>50 || (LIBAVUTIL_VERSION_MAJOR==50 && LIBAVUTIL_VERSION_MINOR>37)
   #include <libavutil/imgutils.h>
@@ -44,11 +41,6 @@ struct camera campreview={
 };
 struct camera* cams=0;
 unsigned int camcount=0;
-#ifdef _WIN32
-  PROCESS_INFORMATION camprocess={.hProcess=0};
-#else
-  pid_t camproc=0;
-#endif
 struct size camsize_out={.width=320, .height=240};
 struct size camsize_scale={.width=320, .height=240};
 GtkWidget* cambox;
@@ -56,6 +48,7 @@ GtkWidget** camrows=0;
 unsigned int camrowcount=0;
 GdkPixbufAnimation* camplaceholder=0;
 GdkPixbufAnimationIter* camplaceholder_iter=0;
+CAM* camout_cam=0;
 
 #if defined(HAVE_AVRESAMPLE) || defined(HAVE_SWRESAMPLE)
 // Experimental mixer, not sure if it really works
@@ -163,6 +156,7 @@ struct camera* camera_new(const char* nick, const char* id)
   gtk_box_pack_start(GTK_BOX(cam->box), eventbox, 0, 0, 0);
   gtk_box_pack_start(GTK_BOX(cam->box), cam->label, 0, 0, 0);
   g_signal_connect(eventbox, "button-release-event", G_CALLBACK(gui_show_cam_menu), cam->id);
+  cam->placeholder=g_timeout_add(100, camplaceholder_update, (char*)id);
   // Initialize postprocessing values
   postproc_init(&cam->postproc);
   return cam;
@@ -180,24 +174,36 @@ void camera_cleanup(void)
 
 void freebuffer(guchar* pixels, gpointer data){free(pixels);}
 
-gboolean cam_encode(GIOChannel* iochannel, GIOCondition condition, gpointer datap)
+unsigned int camout_delay;
+void startcamout(CAM* cam)
+{
+  dprintf(tc_client_in[1], "/camup\n");
+  camout_cam=cam;
+  camout_delay=500;
+  camsize_out.width=320;
+  camsize_out.height=240;
+  cam_resolution(cam, (unsigned int*)&camsize_out.width, (unsigned int*)&camsize_out.height);
+  g_timeout_add(camout_delay, cam_encode, cam);
+}
+
+gboolean cam_encode(void* camera_)
 {
+  CAM* camera=camera_;
+  if(camera!=camout_cam){return G_SOURCE_REMOVE;}
   struct camera* cam=camera_find("out");
-  char preview=0;
-  if(!cam)
+  if(cam->placeholder) // Remove the placeholder animation if it has it
   {
-    cam=&campreview;
-    preview=1;
-  }else{
-    cam->vctx->width=camsize_out.width;
-    cam->vctx->height=camsize_out.height;
-    if(!cam->dstframe->data[0])
-    {
-      cam->dstframe->format=AV_PIX_FMT_YUV420P;
-      cam->dstframe->width=camsize_out.width;
-      cam->dstframe->height=camsize_out.height;
-      av_image_alloc(cam->dstframe->data, cam->dstframe->linesize, camsize_out.width, camsize_out.height, cam->dstframe->format, 1);
-    }
+    g_source_remove(cam->placeholder);
+    cam->placeholder=0;
+  }
+  cam->vctx->width=camsize_out.width;
+  cam->vctx->height=camsize_out.height;
+  if(!cam->dstframe->data[0])
+  {
+    cam->dstframe->format=AV_PIX_FMT_YUV420P;
+    cam->dstframe->width=camsize_out.width;
+    cam->dstframe->height=camsize_out.height;
+    av_image_alloc(cam->dstframe->data, cam->dstframe->linesize, camsize_out.width, camsize_out.height, cam->dstframe->format, 1);
   }
   if(cam->frame->width!=camsize_out.width || cam->frame->height!=camsize_out.height)
   {
@@ -208,28 +214,15 @@ gboolean cam_encode(GIOChannel* iochannel, GIOCondition condition, gpointer data
     av_freep(cam->frame->data);
     av_image_alloc(cam->frame->data, cam->frame->linesize, camsize_out.width, camsize_out.height, cam->frame->format, 1);
   }
-  g_io_channel_read_chars(iochannel, (void*)cam->frame->data[0], camsize_out.width*camsize_out.height*3, 0, 0);
+  cam_getframe(camera, cam->frame->data[0]);
   postprocess(&cam->postproc, cam->frame->data[0], cam->frame->width, cam->frame->height);
   // Update our local display
   GdkPixbuf* oldpixbuf=gtk_image_get_pixbuf(GTK_IMAGE(cam->cam));
   GdkPixbuf* gdkframe=gdk_pixbuf_new_from_data(cam->frame->data[0], GDK_COLORSPACE_RGB, 0, 8, cam->frame->width, cam->frame->height, cam->frame->linesize[0], 0, 0);
-  if(!preview) // Scale, unless we're previewing
-  {
-    gdkframe=gdk_pixbuf_scale_simple(gdkframe, camsize_scale.width, camsize_scale.height, GDK_INTERP_BILINEAR);
-  }else if(gdk_pixbuf_get_height(gdkframe)>PREVIEW_MAX_HEIGHT || gdk_pixbuf_get_width(gdkframe)>PREVIEW_MAX_WIDTH) // Scale anyway if the input is huge
-  {
-    unsigned int width=gdk_pixbuf_get_width(gdkframe);
-    unsigned int height=gdk_pixbuf_get_height(gdkframe);
-    if(height*PREVIEW_MAX_WIDTH/width>PREVIEW_MAX_HEIGHT)
-    {
-      gdkframe=gdk_pixbuf_scale_simple(gdkframe, width*PREVIEW_MAX_HEIGHT/height, PREVIEW_MAX_HEIGHT, GDK_INTERP_BILINEAR);
-    }else{
-      gdkframe=gdk_pixbuf_scale_simple(gdkframe, PREVIEW_MAX_WIDTH, height*PREVIEW_MAX_WIDTH/width, GDK_INTERP_BILINEAR);
-    }
-  }
+  // Scale to fit
+  gdkframe=gdk_pixbuf_scale_simple(gdkframe, camsize_scale.width, camsize_scale.height, GDK_INTERP_BILINEAR);
   gtk_image_set_from_pixbuf(GTK_IMAGE(cam->cam), gdkframe);
   g_object_unref(oldpixbuf);
-  if(preview){return 1;}
   // Encode
   struct SwsContext* swsctx=sws_getContext(cam->frame->width, cam->frame->height, cam->frame->format, cam->frame->width, cam->frame->height, AV_PIX_FMT_YUV420P, SWS_FAST_BILINEAR, 0, 0, 0);
   sws_scale(swsctx, (const uint8_t*const*)cam->frame->data, cam->frame->linesize, 0, cam->frame->height, cam->dstframe->data, cam->dstframe->linesize);
@@ -247,7 +240,7 @@ gboolean cam_encode(GIOChannel* iochannel, GIOCondition condition, gpointer data
   av_init_packet(&packet);
   avcodec_send_frame(cam->vctx, cam->dstframe);
   gotpacket=avcodec_receive_packet(cam->vctx, &packet);
-  if(gotpacket){return 1;}
+  if(gotpacket){return G_SOURCE_CONTINUE;}
   unsigned char frameinfo=0x22; // Note: differentiating between keyframes and non-keyframes seems to break stuff, so let's just go with all being interframes (1=keyframe, 2=interframe, 3=disposable interframe)
   // Send video
   dprintf(tc_client_in[1], "/video %i\n", packet.size+1);
@@ -256,98 +249,98 @@ gboolean cam_encode(GIOChannel* iochannel, GIOCondition condition, gpointer data
 if(w!=packet.size){printf("Error: wrote %li of %i bytes\n", w, packet.size);}
 
   av_packet_unref(&packet);
-  return 1;
+  if(camout_delay>100) // Slowly speed up to 10fps, otherwise the flash client won't show it.
+  {
+    camout_delay-=50;
+    g_timeout_add(camout_delay, cam_encode, camera_);
+    return G_SOURCE_REMOVE;
+  }
+  return G_SOURCE_CONTINUE;
 }
 
-unsigned int* camthread_delay;
-void camthread_resetdelay(int x)
+struct
 {
-  *camthread_delay=500000;
-}
-extern unsigned int cameventsource;
-GIOChannel* camthread(const char* name, AVCodec* vencoder, unsigned int delay)
+  void(*callback)(CAM* cam);
+  void(*cancelcallback)(void);
+  unsigned int eventsource;
+  CAM* current;
+  struct size size;
+} camselect={.eventsource=0, .current=0};
+
+gboolean camselect_frame(void* x)
 {
-  if(camproc)
-  {
-    g_source_remove(cameventsource);
-    kill(camproc, SIGINT);
-    usleep(200000); // Give the previous process some time to shut down
-  }
-  // Set up a pipe to be handled by cam_encode()
-  int campipe[2];
-#ifndef _WIN32
-  CAM* cam=cam_open(name); // Opening here in case of GUI callbacks
-  if(cam){cam_resolution(cam, (unsigned int*)&camsize_out.width, (unsigned int*)&camsize_out.height);}
-  pipe(campipe);
-  camproc=fork();
-  if(!camproc)
+  if(!camselect.current){return G_SOURCE_CONTINUE;}
+  void* buf=malloc(camselect.size.width*camselect.size.height*3);
+  cam_getframe(camselect.current, buf);
+  GdkPixbuf* oldpixbuf=gtk_image_get_pixbuf(GTK_IMAGE(campreview.cam));
+  GdkPixbuf* gdkframe=gdk_pixbuf_new_from_data(buf, GDK_COLORSPACE_RGB, 0, 8, camselect.size.width, camselect.size.height, camselect.size.width*3, 0, freebuffer);
+  if(gdk_pixbuf_get_height(gdkframe)>PREVIEW_MAX_HEIGHT || gdk_pixbuf_get_width(gdkframe)>PREVIEW_MAX_WIDTH) // Scale if the input is huge
   {
-    close(campipe[0]);
-    unsigned char img[camsize_out.width*camsize_out.height*3];
-    if(!cam) // If it failed to open, just give some grey before quitting
-    {
-      memset(img, 0x7f, camsize_out.width*camsize_out.height*3);
-      write(campipe[1], img, camsize_out.width*camsize_out.height*3);
-      _exit(1);
-    }
-#ifndef _WIN32
-    prctl(PR_SET_PDEATHSIG, SIGHUP);
-    camthread_delay=&delay;
-    signal(SIGUSR1, camthread_resetdelay);
-#endif
-    while(1)
+    unsigned int width=gdk_pixbuf_get_width(gdkframe);
+    unsigned int height=gdk_pixbuf_get_height(gdkframe);
+    if(height*PREVIEW_MAX_WIDTH/width>PREVIEW_MAX_HEIGHT)
     {
-      usleep(delay);
-      if(delay>100000){delay-=50000;}
-      cam_getframe(cam, img);
-      write(campipe[1], img, camsize_out.width*camsize_out.height*3);
+      gdkframe=gdk_pixbuf_scale_simple(gdkframe, width*PREVIEW_MAX_HEIGHT/height, PREVIEW_MAX_HEIGHT, GDK_INTERP_BILINEAR);
+    }else{
+      gdkframe=gdk_pixbuf_scale_simple(gdkframe, PREVIEW_MAX_WIDTH, height*PREVIEW_MAX_WIDTH/width, GDK_INTERP_BILINEAR);
     }
-    _exit(0);
   }
-  if(cam){cam_close(cam);} // Leave the cam to the child process
-  close(campipe[1]);
-#else
-  char cmd[snprintf(0,0, "./tc_client-gtk-camthread %s %u", name, delay)+1];
-  sprintf(cmd, "./tc_client-gtk-camthread %s %u", name, delay);
-  w32_runcmdpipes(cmd, ((int*)0), campipe, camprocess);
-#endif
-  GIOChannel* channel=g_io_channel_unix_new(campipe[0]);
-  g_io_channel_set_encoding(channel, 0, 0);
-  return channel;
+  gtk_image_set_from_pixbuf(GTK_IMAGE(campreview.cam), gdkframe);
+  g_object_unref(oldpixbuf);
+  return G_SOURCE_CONTINUE;
 }
 
-void camselect_change(GtkComboBox* combo, AVCodec* vencoder)
+void camselect_open(void(*cb)(CAM*), void(*ccb)(void))
 {
-  if(!camproc){return;} // If there isn't a camthread already, it will be started elsewhere
-  GIOChannel* channel=camthread(gtk_combo_box_get_active_id(combo), vencoder, 100000);
-  cameventsource=g_io_add_watch(channel, G_IO_IN, cam_encode, 0);
+  camselect.callback=cb;
+  camselect.cancelcallback=ccb;
+  // Start the preview
+  if(!camselect.eventsource)
+  {
+    camselect.eventsource=g_timeout_add(100, camselect_frame, 0);
+  }
+  // Open the currently selected camera (usually starting with the top alternative)
+  if(camselect.current){cam_close(camselect.current);}
+  GtkComboBox* combo=GTK_COMBO_BOX(gtk_builder_get_object(gui, "camselect_combo"));
+  camselect_change(combo, 0);
+  GtkWidget* window=GTK_WIDGET(gtk_builder_get_object(gui, "camselection"));
+  gtk_widget_show_all(window);
+}
+
+void camselect_change(GtkComboBox* combo, void* x)
+{
+  if(!camselect.eventsource){return;} // Haven't opened the cam selection window yet (happens at startup)
+  if(camselect.current){cam_close(camselect.current);}
+  camselect.current=cam_open(gtk_combo_box_get_active_id(combo));
+  if(!camselect.current){return;}
+  camselect.size.width=320;
+  camselect.size.height=240;
+  cam_resolution(camselect.current, (unsigned int*)&camselect.size.width, (unsigned int*)&camselect.size.height);
 }
 
 gboolean camselect_cancel(GtkWidget* widget, void* x1, void* x2)
 {
   GtkWidget* window=GTK_WIDGET(gtk_builder_get_object(gui, "camselection"));
   gtk_widget_hide(window);
-  // Note: unchecking the menu item kills the cam thread for us
-  GtkCheckMenuItem* item=GTK_CHECK_MENU_ITEM(gtk_builder_get_object(gui, "menuitem_broadcast_camera"));
-  gtk_check_menu_item_set_active(item, 0);
+  g_source_remove(camselect.eventsource);
+  camselect.eventsource=0;
+  if(camselect.cancelcallback){camselect.cancelcallback();}
   return 1;
 }
 
-extern int tc_client_in[];
-void camselect_accept(GtkWidget* widget, AVCodec* vencoder)
+void camselect_accept(GtkWidget* widget, void* x)
 {
   GtkWidget* window=GTK_WIDGET(gtk_builder_get_object(gui, "camselection"));
   gtk_widget_hide(window);
-#ifndef _WIN32
-  // Tell the camthread to reset the delay to 500000 as a workaround for a quirk in the flash client
-  kill(camproc, SIGUSR1);
-#else
-  // For platforms without proper signals, resort to restarting the camthread with the new delay
-  GtkComboBox* combo=GTK_COMBO_BOX(gtk_builder_get_object(gui, "camselect_combo"));
-  GIOChannel* channel=camthread(gtk_combo_box_get_active_id(combo), vencoder, 500000);
-  cameventsource=g_io_add_watch(channel, G_IO_IN, cam_encode, 0);
-#endif
-  dprintf(tc_client_in[1], "/camup\n");
+  g_source_remove(camselect.eventsource);
+  camselect.eventsource=0;
+  if(camselect.callback)
+  {
+    camselect.callback(camselect.current);
+  }else{
+    cam_close(camselect.current);
+  }
+  camselect.current=0;
 }
 
 void camselect_file_preview(GtkFileChooser* dialog, gpointer data)
diff --git a/utilities/gtk/media.h b/utilities/gtk/media.h
index 58197bc..788b36b 100644
--- a/utilities/gtk/media.h
+++ b/utilities/gtk/media.h
@@ -15,6 +15,7 @@
     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 #include <libavcodec/avcodec.h>
+#include "../libcamera/camera.h"
 #include "postproc.h"
 struct camera
 {
@@ -40,17 +41,12 @@ struct size
 extern struct camera campreview;
 extern struct camera* cams;
 extern unsigned int camcount;
-#ifdef _WIN32
-  extern PROCESS_INFORMATION camprocess;
-  #define camproc camprocess.hProcess
-#else
-  extern pid_t camproc;
-#endif
 extern struct size camsize_out;
 extern struct size camsize_scale;
 extern GtkWidget* cambox;
 extern GdkPixbufAnimation* camplaceholder;
 extern GdkPixbufAnimationIter* camplaceholder_iter;
+extern CAM* camout_cam;
 
 #if defined(HAVE_AVRESAMPLE) || defined(HAVE_SWRESAMPLE)
 extern void camera_playsnd(int audiopipe, struct camera* cam, short* samples, unsigned int samplecount);
@@ -61,11 +57,12 @@ extern struct camera* camera_findbynick(const char* nick);
 extern struct camera* camera_new(const char* nick, const char* id);
 extern void camera_cleanup(void);
 extern void freebuffer(guchar* pixels, gpointer data);
-extern gboolean cam_encode(GIOChannel* iochannel, GIOCondition condition, gpointer datap);
-extern GIOChannel* camthread(const char* name, AVCodec* vencoder, unsigned int delay);
-extern void camselect_change(GtkComboBox* combo, AVCodec* vencoder);
+extern void startcamout(CAM* cam);
+extern gboolean cam_encode(void* camera_);
+extern void camselect_open(void(*cb)(CAM*), void(*ccb)(void));
+extern void camselect_change(GtkComboBox* combo, void* x);
 extern gboolean camselect_cancel(GtkWidget* widget, void* x1, void* x2);
-extern void camselect_accept(GtkWidget* widget, AVCodec* vencoder);
+extern void camselect_accept(GtkWidget* widget, void* x);
 extern const char* camselect_file(void);
 extern void camera_postproc(struct camera* cam, unsigned char* buf, unsigned int width, unsigned int height);
 extern void updatescaling(unsigned int width, unsigned int height, char changedcams);
diff --git a/utilities/libcamera/camera.h b/utilities/libcamera/camera.h
index 8ec37bc..6cf4776 100644
--- a/utilities/libcamera/camera.h
+++ b/utilities/libcamera/camera.h
@@ -14,6 +14,8 @@
     You should have received a copy of the GNU Affero General Public License
     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
+#ifndef CAMERA_H
+#define CAMERA_H
 enum{
   CAMTYPE_V4L2,
   CAMTYPE_ESCAPI,
@@ -28,3 +30,4 @@ extern void cam_resolution(CAM* cam, unsigned int* width, unsigned int* height);
 extern void cam_getframe(CAM* cam, void* pixmap);
 extern void cam_close(CAM* cam);
 extern const char*(*cam_img_filepicker)(void);
+#endif