be GtkApplication, show output in window, snapshot on click
This commit is contained in:
parent
c0d78f0270
commit
e3706e3b4c
3 changed files with 894 additions and 77 deletions
229
focus.c
Normal file
229
focus.c
Normal file
|
@ -0,0 +1,229 @@
|
||||||
|
#include "samples.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function looks up a label or key entry of
|
||||||
|
* a configuration widget.
|
||||||
|
* The functions descend recursively, so you can just
|
||||||
|
* specify the last component.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int
|
||||||
|
_lookup_widget(CameraWidget*widget, const char *key, CameraWidget **child) {
|
||||||
|
int ret;
|
||||||
|
ret = gp_widget_get_child_by_name (widget, key, child);
|
||||||
|
if (ret < GP_OK)
|
||||||
|
ret = gp_widget_get_child_by_label (widget, key, child);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* calls the Nikon DSLR or Canon DSLR autofocus method. */
|
||||||
|
int
|
||||||
|
camera_eosviewfinder(Camera *camera, GPContext *context, int onoff) {
|
||||||
|
CameraWidget *widget = NULL, *child = NULL;
|
||||||
|
CameraWidgetType type;
|
||||||
|
int ret,val;
|
||||||
|
|
||||||
|
ret = gp_camera_get_config (camera, &widget, context);
|
||||||
|
if (ret < GP_OK) {
|
||||||
|
fprintf (stderr, "camera_get_config failed: %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
ret = _lookup_widget (widget, "eosviewfinder", &child);
|
||||||
|
if (ret < GP_OK) {
|
||||||
|
fprintf (stderr, "lookup 'eosviewfinder' failed: %d\n", ret);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check that this is a toggle */
|
||||||
|
ret = gp_widget_get_type (child, &type);
|
||||||
|
if (ret < GP_OK) {
|
||||||
|
fprintf (stderr, "widget get type failed: %d\n", ret);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
switch (type) {
|
||||||
|
case GP_WIDGET_TOGGLE:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fprintf (stderr, "widget has bad type %d\n", type);
|
||||||
|
ret = GP_ERROR_BAD_PARAMETERS;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = gp_widget_get_value (child, &val);
|
||||||
|
if (ret < GP_OK) {
|
||||||
|
fprintf (stderr, "could not get widget value: %d\n", ret);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
val = onoff;
|
||||||
|
ret = gp_widget_set_value (child, &val);
|
||||||
|
if (ret < GP_OK) {
|
||||||
|
fprintf (stderr, "could not set widget value to 1: %d\n", ret);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = gp_camera_set_config (camera, widget, context);
|
||||||
|
if (ret < GP_OK) {
|
||||||
|
fprintf (stderr, "could not set config tree to eosviewfinder: %d\n", ret);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
gp_widget_free (widget);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
camera_auto_focus(Camera *camera, GPContext *context, int onoff) {
|
||||||
|
CameraWidget *widget = NULL, *child = NULL;
|
||||||
|
CameraWidgetType type;
|
||||||
|
int ret,val;
|
||||||
|
|
||||||
|
ret = gp_camera_get_config (camera, &widget, context);
|
||||||
|
if (ret < GP_OK) {
|
||||||
|
fprintf (stderr, "camera_get_config failed: %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
ret = _lookup_widget (widget, "autofocusdrive", &child);
|
||||||
|
if (ret < GP_OK) {
|
||||||
|
fprintf (stderr, "lookup 'autofocusdrive' failed: %d\n", ret);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check that this is a toggle */
|
||||||
|
ret = gp_widget_get_type (child, &type);
|
||||||
|
if (ret < GP_OK) {
|
||||||
|
fprintf (stderr, "widget get type failed: %d\n", ret);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
switch (type) {
|
||||||
|
case GP_WIDGET_TOGGLE:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fprintf (stderr, "widget has bad type %d\n", type);
|
||||||
|
ret = GP_ERROR_BAD_PARAMETERS;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = gp_widget_get_value (child, &val);
|
||||||
|
if (ret < GP_OK) {
|
||||||
|
fprintf (stderr, "could not get widget value: %d\n", ret);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
val = onoff;
|
||||||
|
|
||||||
|
ret = gp_widget_set_value (child, &val);
|
||||||
|
if (ret < GP_OK) {
|
||||||
|
fprintf (stderr, "could not set widget value to 1: %d\n", ret);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = gp_camera_set_config (camera, widget, context);
|
||||||
|
if (ret < GP_OK) {
|
||||||
|
fprintf (stderr, "could not set config tree to autofocus: %d\n", ret);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
gp_widget_free (widget);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Manual focusing a camera...
|
||||||
|
* xx is -3 / -2 / -1 / 0 / 1 / 2 / 3
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
camera_manual_focus (Camera *camera, int xx, GPContext *context) {
|
||||||
|
CameraWidget *widget = NULL, *child = NULL;
|
||||||
|
CameraWidgetType type;
|
||||||
|
int ret;
|
||||||
|
float rval;
|
||||||
|
char *mval;
|
||||||
|
|
||||||
|
ret = gp_camera_get_config (camera, &widget, context);
|
||||||
|
if (ret < GP_OK) {
|
||||||
|
fprintf (stderr, "camera_get_config failed: %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
ret = _lookup_widget (widget, "manualfocusdrive", &child);
|
||||||
|
if (ret < GP_OK) {
|
||||||
|
fprintf (stderr, "lookup 'manualfocusdrive' failed: %d\n", ret);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check that this is a toggle */
|
||||||
|
ret = gp_widget_get_type (child, &type);
|
||||||
|
if (ret < GP_OK) {
|
||||||
|
fprintf (stderr, "widget get type failed: %d\n", ret);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
switch (type) {
|
||||||
|
case GP_WIDGET_RADIO: {
|
||||||
|
int choices = gp_widget_count_choices (child);
|
||||||
|
|
||||||
|
ret = gp_widget_get_value (child, &mval);
|
||||||
|
if (ret < GP_OK) {
|
||||||
|
fprintf (stderr, "could not get widget value: %d\n", ret);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (choices == 7) { /* see what Canon has in EOS_MFDrive */
|
||||||
|
ret = gp_widget_get_choice (child, xx+4, (const char**)&mval);
|
||||||
|
if (ret < GP_OK) {
|
||||||
|
fprintf (stderr, "could not get widget choice %d: %d\n", xx+2, ret);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
fprintf(stderr,"manual focus %d -> %s\n", xx, mval);
|
||||||
|
}
|
||||||
|
ret = gp_widget_set_value (child, mval);
|
||||||
|
if (ret < GP_OK) {
|
||||||
|
fprintf (stderr, "could not set widget value to 1: %d\n", ret);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GP_WIDGET_RANGE:
|
||||||
|
ret = gp_widget_get_value (child, &rval);
|
||||||
|
if (ret < GP_OK) {
|
||||||
|
fprintf (stderr, "could not get widget value: %d\n", ret);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (xx) { /* Range is on Nikon from -32768 <-> 32768 */
|
||||||
|
case -3: rval = -1024;break;
|
||||||
|
case -2: rval = -512;break;
|
||||||
|
case -1: rval = -128;break;
|
||||||
|
case 0: rval = 0;break;
|
||||||
|
case 1: rval = 128;break;
|
||||||
|
case 2: rval = 512;break;
|
||||||
|
case 3: rval = 1024;break;
|
||||||
|
|
||||||
|
default: rval = xx; break; /* hack */
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr,"manual focus %d -> %f\n", xx, rval);
|
||||||
|
|
||||||
|
ret = gp_widget_set_value (child, &rval);
|
||||||
|
if (ret < GP_OK) {
|
||||||
|
fprintf (stderr, "could not set widget value to 1: %d\n", ret);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fprintf (stderr, "widget has bad type %d\n", type);
|
||||||
|
ret = GP_ERROR_BAD_PARAMETERS;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ret = gp_camera_set_config (camera, widget, context);
|
||||||
|
if (ret < GP_OK) {
|
||||||
|
fprintf (stderr, "could not set config tree to autofocus: %d\n", ret);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
gp_widget_free (widget);
|
||||||
|
return ret;
|
||||||
|
}
|
668
photobooth.c
668
photobooth.c
|
@ -13,47 +13,69 @@
|
||||||
* distributed other than under the conditions noted above.
|
* distributed other than under the conditions noted above.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// gcc -Wall -g `pkg-config gstreamer-1.0 gstreamer-video-1.0 libgphoto2 --cflags --libs gtk+-3.0 gtk+-x11-3.0` photobooth.c -o photobooth
|
// gcc -Wall -g `pkg-config gstreamer-1.0 gstreamer-video-1.0 libgphoto2 gtk+-3.0 gtk+-x11-3.0 --cflags --libs` photobooth.c focus.c -o photobooth
|
||||||
|
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/un.h>
|
#include <sys/un.h>
|
||||||
#include <gtk/gtk.h>
|
|
||||||
#include <gdk/gdkx.h>
|
#include <gdk/gdkx.h>
|
||||||
#include <gst/video/videooverlay.h>
|
#include <gst/video/videooverlay.h>
|
||||||
#include "photobooth.h"
|
#include "photobooth.h"
|
||||||
|
|
||||||
#define photo_booth_parent_class parent_class
|
#define photo_booth_parent_class parent_class
|
||||||
G_DEFINE_TYPE (PhotoBooth, photo_booth, G_TYPE_OBJECT);
|
G_DEFINE_TYPE (PhotoBooth, photo_booth, GTK_TYPE_APPLICATION);
|
||||||
|
|
||||||
GST_DEBUG_CATEGORY_STATIC (photo_booth_debug);
|
GST_DEBUG_CATEGORY_STATIC (photo_booth_debug);
|
||||||
#define GST_CAT_DEFAULT photo_booth_debug
|
#define GST_CAT_DEFAULT photo_booth_debug
|
||||||
|
|
||||||
gboolean _pb_cam_init (CameraInfo *cam_info);
|
/* GObject / GApplication */
|
||||||
gboolean _pb_cam_close (CameraInfo *cam_info);
|
static void photo_booth_activate (GApplication *app);
|
||||||
void _pb_flush_pipe (int fd);
|
static void photo_booth_open (GApplication *app, GFile **files, gint n_files, const gchar *hint);
|
||||||
static void _pb_video_capture_thread_func (PhotoBooth *pb);
|
static void photo_booth_finalize (GObject *object);
|
||||||
|
PhotoBooth *photo_booth_new (void);
|
||||||
|
static void photo_booth_clicked (GtkWidget *button, GdkEventButton *event, PhotoBooth *pb);
|
||||||
|
|
||||||
static void photo_booth_finalize (GObject * object);
|
/* general private functions */
|
||||||
|
static void photo_booth_quit_signal (GMainLoop *loop);
|
||||||
|
static void photo_booth_window_destroyed_signal (PhotoBoothWindow *win, GMainLoop *loop);
|
||||||
|
static void photo_booth_setup_window (PhotoBooth *pb);
|
||||||
|
static void photo_booth_preview (PhotoBooth *pb);
|
||||||
|
static void photo_booth_snapshot (PhotoBooth *pb);
|
||||||
|
|
||||||
|
/* libgphoto2 */
|
||||||
|
static gboolean photo_booth_cam_init (CameraInfo **cam_info);
|
||||||
|
static gboolean photo_booth_cam_close (CameraInfo **cam_info);
|
||||||
|
static void photo_booth_flush_pipe (int fd);
|
||||||
|
static void photo_booth_video_capture_thread_func (PhotoBooth *pb);
|
||||||
|
|
||||||
|
/* gstreamer functions */
|
||||||
|
static gboolean photo_booth_setup_gstreamer (PhotoBooth *pb);
|
||||||
|
static GstBusSyncReply photo_booth_bus_sync_callback (GstBus *bus, GstMessage *message, GtkWidget *drawing_area);
|
||||||
|
static gboolean photo_booth_bus_callback (GstBus *bus, GstMessage *message, PhotoBooth *pb);
|
||||||
|
|
||||||
static void photo_booth_class_init (PhotoBoothClass *klass)
|
static void photo_booth_class_init (PhotoBoothClass *klass)
|
||||||
{
|
{
|
||||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||||
|
GApplicationClass *gapplication_class = G_APPLICATION_CLASS (klass);
|
||||||
|
|
||||||
GST_DEBUG_CATEGORY_INIT (photo_booth_debug, "photobooth", GST_DEBUG_BOLD | GST_DEBUG_FG_YELLOW | GST_DEBUG_BG_BLUE, "PhotoBooth");
|
GST_DEBUG_CATEGORY_INIT (photo_booth_debug, "photobooth", GST_DEBUG_BOLD | GST_DEBUG_FG_YELLOW | GST_DEBUG_BG_BLUE, "PhotoBooth");
|
||||||
GST_DEBUG ("photo_booth_class_init");
|
GST_DEBUG ("photo_booth_class_init");
|
||||||
gobject_class->finalize = photo_booth_finalize;
|
gobject_class->finalize = photo_booth_finalize;
|
||||||
|
gapplication_class->activate = photo_booth_activate;
|
||||||
|
gapplication_class->open = photo_booth_open;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void photo_booth_init (PhotoBooth *pb)
|
static void photo_booth_init (PhotoBooth *pb)
|
||||||
{
|
{
|
||||||
GST_DEBUG_OBJECT (pb, "photo_booth_init init object!");
|
GST_DEBUG_OBJECT (pb, "photo_booth_init init object!");
|
||||||
|
|
||||||
int control_sock[2];
|
int control_sock[2];
|
||||||
if (socketpair (PF_UNIX, SOCK_STREAM, 0, control_sock) < 0)
|
if (socketpair (PF_UNIX, SOCK_STREAM, 0, control_sock) < 0)
|
||||||
{
|
{
|
||||||
|
@ -65,51 +87,117 @@ static void photo_booth_init (PhotoBooth *pb)
|
||||||
fcntl (READ_SOCKET (pb), F_SETFL, O_NONBLOCK);
|
fcntl (READ_SOCKET (pb), F_SETFL, O_NONBLOCK);
|
||||||
fcntl (WRITE_SOCKET (pb), F_SETFL, O_NONBLOCK);
|
fcntl (WRITE_SOCKET (pb), F_SETFL, O_NONBLOCK);
|
||||||
|
|
||||||
if (!_pb_cam_init (&pb->cam_info))
|
if (!photo_booth_cam_init (&pb->cam_info))
|
||||||
g_error ("can't init cam!");
|
GST_ERROR_OBJECT (pb, "can't init cam!");
|
||||||
|
|
||||||
|
pb->state = PB_STATE_NONE;
|
||||||
|
pb->video_block_id = 0;
|
||||||
|
pb->photo_block_id = 0;
|
||||||
|
|
||||||
pb->video_capture_thread = NULL;
|
pb->video_capture_thread = NULL;
|
||||||
pb->video_capture_thread = g_thread_try_new ("video-capture", (GThreadFunc) _pb_video_capture_thread_func, pb, NULL);
|
pb->video_capture_thread = g_thread_try_new ("video-capture", (GThreadFunc) photo_booth_video_capture_thread_func, pb, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void photo_booth_finalize (GObject * object)
|
static void photo_booth_setup_window (PhotoBooth *pb)
|
||||||
|
{
|
||||||
|
PhotoBoothWindow *win = photo_booth_window_new (pb);
|
||||||
|
gtk_window_present (GTK_WINDOW (win));
|
||||||
|
GdkScreen *screen = gdk_screen_get_default ();
|
||||||
|
gtk_window_fullscreen_on_monitor (GTK_WINDOW (win), screen, 0);
|
||||||
|
GdkWindow *w = gdk_screen_get_active_window (screen);
|
||||||
|
gint m = gdk_screen_get_monitor_at_window (screen, w);
|
||||||
|
gdk_screen_get_monitor_geometry (screen, m, &pb->monitor_geo);
|
||||||
|
GST_INFO_OBJECT (pb, "monitor geometry %dx%d", pb->monitor_geo.width, pb->monitor_geo.height);
|
||||||
|
g_signal_connect (G_OBJECT (win), "destroy", G_CALLBACK (photo_booth_window_destroyed_signal), pb->loop);
|
||||||
|
pb->overlay = gtk_overlay_new ();
|
||||||
|
gtk_container_add (GTK_CONTAINER (win), pb->overlay);
|
||||||
|
pb->drawing_area = gtk_drawing_area_new ();
|
||||||
|
gtk_container_add (GTK_CONTAINER (pb->overlay), pb->drawing_area);
|
||||||
|
g_signal_connect (G_OBJECT (pb->drawing_area), "button-press-event", G_CALLBACK (photo_booth_clicked), pb);
|
||||||
|
gtk_widget_add_events (pb->drawing_area, GDK_BUTTON_PRESS_MASK);
|
||||||
|
gtk_widget_show_all (pb->overlay);
|
||||||
|
gtk_widget_show_all (GTK_WIDGET (win));
|
||||||
|
photo_booth_setup_gstreamer (pb);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void photo_booth_activate (GApplication *app)
|
||||||
|
{
|
||||||
|
GST_DEBUG_OBJECT (app, "photo_booth_activate");
|
||||||
|
photo_booth_setup_window (PHOTO_BOOTH (app));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void photo_booth_open (GApplication *app, GFile **files, gint n_files, const gchar *hint)
|
||||||
|
{
|
||||||
|
GST_DEBUG_OBJECT (app, "photo_booth_open");
|
||||||
|
photo_booth_setup_window (PHOTO_BOOTH (app));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void photo_booth_finalize (GObject *object)
|
||||||
{
|
{
|
||||||
PhotoBooth *pb = PHOTO_BOOTH (object);
|
PhotoBooth *pb = PHOTO_BOOTH (object);
|
||||||
GST_INFO_OBJECT (pb, "finalize server");
|
GST_INFO_OBJECT (pb, "finalize server");
|
||||||
SEND_COMMAND (pb, CONTROL_STOP);
|
SEND_COMMAND (pb, CONTROL_STOP);
|
||||||
_pb_flush_pipe (pb->video_fd);
|
photo_booth_flush_pipe (pb->video_fd);
|
||||||
g_thread_join (pb->video_capture_thread);
|
g_thread_join (pb->video_capture_thread);
|
||||||
_pb_cam_close (&pb->cam_info);
|
if (pb->cam_info)
|
||||||
|
photo_booth_cam_close (&pb->cam_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _gphoto_gst_errordumper(GPLogLevel level, const char *domain, const char *str, void *data)
|
static void _gphoto_gst_errordumper(GPLogLevel level, const char *domain, const char *str, void *data)
|
||||||
{
|
{
|
||||||
GST_DEBUG ("GPhoto %d, %s:%s", (int) level, domain, str);
|
GST_DEBUG ("GPhoto %d, %s:%s", (int) level, domain, str);
|
||||||
}
|
}
|
||||||
|
|
||||||
gboolean _pb_cam_init (CameraInfo *cam_info)
|
static GstPadProbeReturn _gst_photo_probecb (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
|
||||||
|
{
|
||||||
|
GST_DEBUG_OBJECT (pad, "drop photo");
|
||||||
|
return GST_PAD_PROBE_DROP;
|
||||||
|
}
|
||||||
|
static GstPadProbeReturn _gst_video_probecb (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
|
||||||
|
{
|
||||||
|
GST_DEBUG_OBJECT (pad, "drop video");
|
||||||
|
return GST_PAD_PROBE_DROP;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean photo_booth_cam_init (CameraInfo **cam_info)
|
||||||
{
|
{
|
||||||
int retval;
|
int retval;
|
||||||
|
*cam_info = (CameraInfo*)malloc(sizeof(struct _CameraInfo));
|
||||||
|
if (!cam_info)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
cam_info->context = gp_context_new();
|
g_mutex_init (&(*cam_info)->mutex);
|
||||||
|
g_mutex_lock (&(*cam_info)->mutex);
|
||||||
|
(*cam_info)->preview_capture_count = 0;
|
||||||
|
(*cam_info)->context = gp_context_new();
|
||||||
gp_log_add_func(GP_LOG_ERROR, _gphoto_gst_errordumper, NULL);
|
gp_log_add_func(GP_LOG_ERROR, _gphoto_gst_errordumper, NULL);
|
||||||
gp_camera_new(&cam_info->camera);
|
gp_camera_new(&(*cam_info)->camera);
|
||||||
retval = gp_camera_init(cam_info->camera, cam_info->context);
|
retval = gp_camera_init((*cam_info)->camera, (*cam_info)->context);
|
||||||
GST_DEBUG ("gp_camera_init returned %d", retval);
|
GST_DEBUG ("gp_camera_init returned %d cam_info@%p camera@%p", retval, *cam_info, (*cam_info)->camera);
|
||||||
|
g_mutex_unlock (&(*cam_info)->mutex);
|
||||||
if (retval != GP_OK) {
|
if (retval != GP_OK) {
|
||||||
|
g_mutex_clear (&(*cam_info)->mutex);
|
||||||
|
free (*cam_info);
|
||||||
|
*cam_info = NULL;
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
gboolean _pb_cam_close (CameraInfo *cam_info)
|
static gboolean photo_booth_cam_close (CameraInfo **cam_info)
|
||||||
{
|
{
|
||||||
int retval;
|
int retval;
|
||||||
retval = gp_camera_exit(cam_info->camera, cam_info->context);
|
g_mutex_lock (&(*cam_info)->mutex);
|
||||||
|
retval = gp_camera_exit((*cam_info)->camera, (*cam_info)->context);
|
||||||
GST_DEBUG ("gp_camera_exit returned %i", retval);
|
GST_DEBUG ("gp_camera_exit returned %i", retval);
|
||||||
|
g_mutex_unlock (&(*cam_info)->mutex);
|
||||||
|
g_mutex_clear (&(*cam_info)->mutex);
|
||||||
|
free (*cam_info);
|
||||||
|
*cam_info = NULL;
|
||||||
return GP_OK ? TRUE : FALSE;
|
return GP_OK ? TRUE : FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _pb_flush_pipe (int fd)
|
static void photo_booth_flush_pipe (int fd)
|
||||||
{
|
{
|
||||||
int rlen = 0;
|
int rlen = 0;
|
||||||
unsigned char buf[1024];
|
unsigned char buf[1024];
|
||||||
|
@ -117,23 +205,30 @@ void _pb_flush_pipe (int fd)
|
||||||
while (rlen != -1)
|
while (rlen != -1)
|
||||||
rlen = read (fd, buf, sizeof(buf));
|
rlen = read (fd, buf, sizeof(buf));
|
||||||
}
|
}
|
||||||
void _pb_quit_signal (GMainLoop *loop)
|
|
||||||
|
static void photo_booth_quit_signal (GMainLoop *loop)
|
||||||
{
|
{
|
||||||
GST_INFO_OBJECT (loop, "caught SIGINT");
|
GST_INFO_OBJECT (loop, "caught SIGINT");
|
||||||
g_main_loop_quit (loop);
|
g_main_loop_quit (loop);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _pb_video_capture_thread_func (PhotoBooth *pb)
|
static void photo_booth_window_destroyed_signal (PhotoBoothWindow *win, GMainLoop *loop)
|
||||||
|
{
|
||||||
|
GST_INFO_OBJECT (win, "window destroyed");
|
||||||
|
g_main_loop_quit (loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void photo_booth_video_capture_thread_func (PhotoBooth *pb)
|
||||||
{
|
{
|
||||||
PhotoboothReadthreadState state = CAPTURETHREAD_NONE;
|
PhotoboothReadthreadState state = CAPTURETHREAD_NONE;
|
||||||
|
|
||||||
mkfifo("moviepipe", 0666);
|
mkfifo("moviepipe", 0666);
|
||||||
pb->video_fd = open("moviepipe", O_RDWR);
|
pb->video_fd = open("moviepipe", O_RDWR);
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (pb, "enter video capture thread fd = %d", pb->video_fd);
|
GST_DEBUG_OBJECT (pb, "enter video capture thread fd = %d cam_info@%p", pb->video_fd, pb->cam_info);
|
||||||
|
|
||||||
CameraFile *gp_file = NULL;
|
CameraFile *gp_file = NULL;
|
||||||
int gp_r, captured_frames = 0;
|
int gpret, captured_frames = 0;
|
||||||
|
|
||||||
if (gp_file_new_from_fd (&gp_file, pb->video_fd) != GP_OK)
|
if (gp_file_new_from_fd (&gp_file, pb->video_fd) != GP_OK)
|
||||||
{
|
{
|
||||||
|
@ -146,9 +241,22 @@ static void _pb_video_capture_thread_func (PhotoBooth *pb)
|
||||||
goto stop_running;
|
goto stop_running;
|
||||||
|
|
||||||
struct pollfd rfd[2];
|
struct pollfd rfd[2];
|
||||||
int timeout = 1000 / PREVIEW_FPS;
|
int timeout = 0;
|
||||||
rfd[0].fd = READ_SOCKET (pb);
|
rfd[0].fd = READ_SOCKET (pb);
|
||||||
rfd[0].events = POLLIN | POLLERR | POLLHUP | POLLPRI;
|
rfd[0].events = POLLIN | POLLERR | POLLHUP | POLLPRI;
|
||||||
|
|
||||||
|
if (!pb->cam_info)
|
||||||
|
{
|
||||||
|
if (!photo_booth_cam_init (&pb->cam_info))
|
||||||
|
GST_DEBUG_OBJECT (pb, "no camera info - can't capture!");
|
||||||
|
timeout = 10000;
|
||||||
|
}
|
||||||
|
else if (pb->state == PB_STATE_NONE)
|
||||||
|
pb->state = PB_STATE_PREVIEW;
|
||||||
|
else if (state == CAPTURETHREAD_PAUSED)
|
||||||
|
timeout = 1000;
|
||||||
|
else
|
||||||
|
timeout = 1000 / PREVIEW_FPS;
|
||||||
|
|
||||||
int ret = poll(rfd, 1, timeout);
|
int ret = poll(rfd, 1, timeout);
|
||||||
if (G_UNLIKELY (ret == -1))
|
if (G_UNLIKELY (ret == -1))
|
||||||
|
@ -156,24 +264,34 @@ static void _pb_video_capture_thread_func (PhotoBooth *pb)
|
||||||
GST_ERROR_OBJECT (pb, "SELECT ERROR!");
|
GST_ERROR_OBJECT (pb, "SELECT ERROR!");
|
||||||
goto stop_running;
|
goto stop_running;
|
||||||
}
|
}
|
||||||
else if ( ret == 0 )
|
else if (ret == 0 && state >= CAPTURETHREAD_RUN)
|
||||||
{
|
{
|
||||||
const char *mime;
|
const char *mime;
|
||||||
gp_r = gp_camera_capture_preview (pb->cam_info.camera, gp_file, pb->cam_info.context);
|
if (pb->cam_info)
|
||||||
if (gp_r < 0) {
|
{
|
||||||
GST_ERROR_OBJECT (pb, "Movie capture error.");
|
g_mutex_lock (&pb->cam_info->mutex);
|
||||||
state = CAPTURETHREAD_STOP;
|
gpret = gp_camera_capture_preview (pb->cam_info->camera, gp_file, pb->cam_info->context);
|
||||||
break;
|
g_mutex_unlock (&pb->cam_info->mutex);
|
||||||
|
if (gpret < 0) {
|
||||||
|
GST_ERROR_OBJECT (pb, "Movie capture error %d", gpret);
|
||||||
|
state = CAPTURETHREAD_STOP;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
g_mutex_lock (&pb->cam_info->mutex);
|
||||||
|
gp_file_get_mime_type (gp_file, &mime);
|
||||||
|
g_mutex_unlock (&pb->cam_info->mutex);
|
||||||
|
if (strcmp (mime, GP_MIME_JPEG)) {
|
||||||
|
GST_ERROR_OBJECT ("Movie capture error... Unhandled MIME type '%s'.", mime);
|
||||||
|
state = CAPTURETHREAD_STOP;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
captured_frames++;
|
||||||
|
GST_DEBUG_OBJECT (pb, "captured frame (%d frames total)", captured_frames);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
gp_file_get_mime_type (gp_file, &mime);
|
|
||||||
if (strcmp (mime, GP_MIME_JPEG)) {
|
|
||||||
g_print ("Movie capture error... Unhandled MIME type '%s'.", mime);
|
|
||||||
state = CAPTURETHREAD_STOP;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
captured_frames++;
|
|
||||||
}
|
}
|
||||||
else if ( rfd[0].revents )
|
else if (rfd[0].revents)
|
||||||
{
|
{
|
||||||
char command;
|
char command;
|
||||||
READ_COMMAND (pb, command, ret);
|
READ_COMMAND (pb, command, ret);
|
||||||
|
@ -182,6 +300,10 @@ static void _pb_video_capture_thread_func (PhotoBooth *pb)
|
||||||
GST_DEBUG_OBJECT (pb, "CONTROL_STOP!");
|
GST_DEBUG_OBJECT (pb, "CONTROL_STOP!");
|
||||||
state = CAPTURETHREAD_STOP;
|
state = CAPTURETHREAD_STOP;
|
||||||
break;
|
break;
|
||||||
|
case CONTROL_PAUSE:
|
||||||
|
GST_DEBUG_OBJECT (pb, "CONTROL_PAUSE!");
|
||||||
|
state = CAPTURETHREAD_PAUSED;
|
||||||
|
break;
|
||||||
case CONTROL_RUN:
|
case CONTROL_RUN:
|
||||||
GST_DEBUG_OBJECT (pb, "CONTROL_RUN");
|
GST_DEBUG_OBJECT (pb, "CONTROL_RUN");
|
||||||
state = CAPTURETHREAD_RUN;
|
state = CAPTURETHREAD_RUN;
|
||||||
|
@ -191,8 +313,12 @@ static void _pb_video_capture_thread_func (PhotoBooth *pb)
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
else if (state == CAPTURETHREAD_PAUSED)
|
||||||
|
{
|
||||||
|
GST_DEBUG_OBJECT (pb, "captured thread paused... timeout");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
g_assert_not_reached ();
|
g_assert_not_reached ();
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -208,23 +334,441 @@ static void _pb_video_capture_thread_func (PhotoBooth *pb)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main (int argc, char *argv[])
|
static GstElement *build_video_bin (PhotoBooth *pb)
|
||||||
{
|
{
|
||||||
GMainLoop *loop;
|
GstElement *video_bin;
|
||||||
PhotoBooth *pb;
|
GstElement *mjpeg_source, *mjpeg_decoder, *mjpeg_filter, *video_filter, *video_scale, *video_convert;
|
||||||
|
GstCaps *caps;
|
||||||
gst_init (&argc, &argv);
|
GstPad *ghost, *pad;
|
||||||
gtk_init (0, NULL);
|
|
||||||
|
|
||||||
loop = g_main_loop_new (NULL, FALSE);
|
|
||||||
|
|
||||||
g_unix_signal_add (SIGINT, (GSourceFunc) _pb_quit_signal, loop);
|
|
||||||
|
|
||||||
pb = g_object_new (PHOTO_BOOTH_TYPE, NULL);
|
video_bin = gst_element_factory_make ("bin", "videobin");
|
||||||
|
mjpeg_source = gst_element_factory_make ("fdsrc", "mjpeg-fdsrc");
|
||||||
|
g_object_set (mjpeg_source, "fd", pb->video_fd, NULL);
|
||||||
|
g_object_set (mjpeg_source, "do-timestamp", TRUE, NULL);
|
||||||
|
g_object_set (mjpeg_source, "blocksize", 65536, NULL);
|
||||||
|
|
||||||
|
mjpeg_filter = gst_element_factory_make ("capsfilter", "mjpeg-capsfilter");
|
||||||
|
caps = gst_caps_new_simple ("image/jpeg", "width", G_TYPE_INT, 640, "height", G_TYPE_INT, 424, "framerate", GST_TYPE_FRACTION, PREVIEW_FPS, 1, "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1, NULL);
|
||||||
|
g_object_set (G_OBJECT (mjpeg_filter), "caps", caps, NULL);
|
||||||
|
gst_caps_unref (caps);
|
||||||
|
|
||||||
|
mjpeg_decoder = gst_element_factory_make ("jpegdec", "mjpeg-decoder");
|
||||||
|
video_scale = gst_element_factory_make ("videoscale", "mjpeg-videoscale");
|
||||||
|
video_convert = gst_element_factory_make ("videoconvert", "mjpeg-videoconvert");
|
||||||
|
video_filter = gst_element_factory_make ("capsfilter", "video-capsfilter");
|
||||||
|
caps = gst_caps_new_simple ("video/x-raw", "width", G_TYPE_INT, pb->monitor_geo.width, "height", G_TYPE_INT, pb->monitor_geo.height, NULL);
|
||||||
|
g_object_set (G_OBJECT (video_filter), "caps", caps, NULL);
|
||||||
|
gst_caps_unref (caps);
|
||||||
|
|
||||||
|
if (!(mjpeg_source && mjpeg_filter && mjpeg_decoder && video_scale && video_convert && video_filter))
|
||||||
|
{
|
||||||
|
GST_ERROR_OBJECT (video_bin, "Failed to videobin pipeline element(s):%s%s%s%s%s%s", mjpeg_source?"":" fdsrc", mjpeg_filter?"":" capsfilter", mjpeg_decoder?"":" jpegdec",
|
||||||
|
video_scale?"":" videoscale", video_convert?"":" videoconvert", video_filter?"":" capsfilter");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gst_bin_add_many (GST_BIN (video_bin), mjpeg_source, mjpeg_filter, mjpeg_decoder, video_scale, video_convert, video_filter, NULL);
|
||||||
|
|
||||||
|
if (!gst_element_link_many (mjpeg_source, mjpeg_filter, mjpeg_decoder, video_scale, video_convert, video_filter, NULL))
|
||||||
|
{
|
||||||
|
GST_ERROR_OBJECT(video_bin, "couldn't link videobin elements!");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
pad = gst_element_get_static_pad (video_filter, "src");
|
||||||
|
ghost = gst_ghost_pad_new ("src", pad);
|
||||||
|
gst_object_unref (pad);
|
||||||
|
gst_pad_set_active (ghost, TRUE);
|
||||||
|
gst_element_add_pad (video_bin, ghost);
|
||||||
|
return video_bin;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstElement *build_photo_bin (PhotoBooth *pb)
|
||||||
|
{
|
||||||
|
GstElement *photo_bin;
|
||||||
|
GstElement *photo_source, *photo_decoder, *photo_freeze, *photo_scale, *photo_filter;
|
||||||
|
GstCaps *caps;
|
||||||
|
GstPad *ghost, *pad;
|
||||||
|
|
||||||
|
photo_bin = gst_element_factory_make ("bin", "photobin");
|
||||||
|
photo_source = gst_element_factory_make ("appsrc", "photo-appsrc");
|
||||||
|
photo_decoder = gst_element_factory_make ("jpegdec", "photo-decoder");
|
||||||
|
photo_freeze = gst_element_factory_make ("imagefreeze", "photo-freeze");
|
||||||
|
photo_scale = gst_element_factory_make ("videoscale", "photo-scale");
|
||||||
|
|
||||||
|
photo_filter = gst_element_factory_make ("capsfilter", "photo-filter");
|
||||||
|
caps = gst_caps_new_simple ("video/x-raw", "width", G_TYPE_INT, pb->monitor_geo.width, "height", G_TYPE_INT, pb->monitor_geo.height, "framerate", GST_TYPE_FRACTION, 1, 10, NULL);
|
||||||
|
g_object_set (G_OBJECT (photo_filter), "caps", caps, NULL);
|
||||||
|
gst_caps_unref (caps);
|
||||||
|
|
||||||
|
if (!(photo_bin && photo_source && photo_decoder && photo_freeze && photo_scale && photo_filter))
|
||||||
|
{
|
||||||
|
GST_ERROR_OBJECT (photo_bin, "Failed to photobin pipeline element(s)");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gst_bin_add_many (GST_BIN (photo_bin), photo_source, photo_decoder, photo_freeze, photo_scale, photo_filter, NULL);
|
||||||
|
|
||||||
|
if (!gst_element_link_many (photo_source, photo_decoder, photo_freeze, photo_scale, photo_filter, NULL))
|
||||||
|
{
|
||||||
|
GST_ERROR_OBJECT(photo_bin, "couldn't link photobin elements!");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
pad = gst_element_get_static_pad (photo_filter, "src");
|
||||||
|
ghost = gst_ghost_pad_new ("src", pad);
|
||||||
|
gst_object_unref (pad);
|
||||||
|
gst_pad_set_active (ghost, TRUE);
|
||||||
|
gst_element_add_pad (photo_bin, ghost);
|
||||||
|
return photo_bin;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean photo_booth_setup_gstreamer (PhotoBooth *pb)
|
||||||
|
{
|
||||||
|
GstBus * bus;
|
||||||
|
|
||||||
|
pb->video_bin = build_video_bin (pb);
|
||||||
|
pb->photo_bin = build_photo_bin (pb);
|
||||||
|
|
||||||
|
pb->pipeline = gst_pipeline_new ("photobooth-pipeline");
|
||||||
|
|
||||||
|
pb->pixoverlay = gst_element_factory_make ("gdkpixbufoverlay", NULL);
|
||||||
|
g_object_set (pb->pixoverlay, "location", "overlay_print.png", NULL);
|
||||||
|
g_object_set (pb->pixoverlay, "overlay-width", pb->monitor_geo.width, NULL);
|
||||||
|
g_object_set (pb->pixoverlay, "overlay-height", pb->monitor_geo.height, NULL);
|
||||||
|
|
||||||
|
pb->video_sink = gst_element_factory_make ("xvimagesink", NULL);
|
||||||
|
// g_object_set (pb->video_sink, "sync", FALSE, NULL);
|
||||||
|
|
||||||
|
if (!(pb->pixoverlay && pb->video_sink))
|
||||||
|
{
|
||||||
|
GST_ERROR_OBJECT (pb, "Failed to create pipeline element(s):%s%s", pb->pixoverlay?"":" gdkpixbufoverlay", pb->video_sink?"":" xvimagesink");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gst_bin_add_many (GST_BIN (pb->pipeline), pb->pixoverlay, pb->video_sink, NULL);
|
||||||
|
|
||||||
|
if (!gst_element_link_many (pb->pixoverlay, pb->video_sink, NULL))
|
||||||
|
{
|
||||||
|
GST_ERROR_OBJECT(pb, "couldn't link elements!");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gst_element_set_state (pb->pipeline, GST_STATE_PLAYING);
|
||||||
|
|
||||||
|
gst_bin_add_many (GST_BIN (pb->pipeline), pb->video_bin, pb->photo_bin, NULL);
|
||||||
|
|
||||||
|
bus = gst_pipeline_get_bus (GST_PIPELINE (pb->pipeline));
|
||||||
|
|
||||||
|
/* add watch for messages */
|
||||||
|
gst_bus_add_watch (bus, (GstBusFunc) photo_booth_bus_callback, pb);
|
||||||
|
gst_bus_set_sync_handler (bus, (GstBusSyncHandler) photo_booth_bus_sync_callback, pb->drawing_area, NULL);
|
||||||
|
|
||||||
|
photo_booth_preview (pb);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstBusSyncReply photo_booth_bus_sync_callback (GstBus *bus, GstMessage *message, GtkWidget *drawing_area)
|
||||||
|
{
|
||||||
|
if (GST_MESSAGE_TYPE (message) != GST_MESSAGE_ELEMENT)
|
||||||
|
return GST_BUS_PASS;
|
||||||
|
|
||||||
|
if (!gst_message_has_name (message, "prepare-window-handle"))
|
||||||
|
return GST_BUS_PASS;
|
||||||
|
|
||||||
|
/* FIXME: make sure to get XID in main thread */
|
||||||
|
GST_DEBUG_OBJECT (drawing_area, "setting video overlay window handle");
|
||||||
|
gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (message->src), GDK_WINDOW_XID (gtk_widget_get_window (drawing_area)));
|
||||||
|
|
||||||
|
gst_message_unref (message);
|
||||||
|
return GST_BUS_DROP;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean photo_booth_bus_callback (GstBus *bus, GstMessage *message, PhotoBooth *pb)
|
||||||
|
{
|
||||||
|
switch (GST_MESSAGE_TYPE (message))
|
||||||
|
{
|
||||||
|
case GST_MESSAGE_WARNING:
|
||||||
|
{
|
||||||
|
GError *err = NULL;
|
||||||
|
gchar *debug = NULL;
|
||||||
|
|
||||||
|
gst_message_parse_warning (message, &err, &debug);
|
||||||
|
GST_WARNING ("Warning: %s\n", err->message);
|
||||||
|
g_error_free (err);
|
||||||
|
g_free (debug);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GST_MESSAGE_ERROR:
|
||||||
|
{
|
||||||
|
GError *err = NULL;
|
||||||
|
gchar *debug = NULL;
|
||||||
|
|
||||||
|
gst_message_parse_error (message, &err, &debug);
|
||||||
|
GST_ERROR ("Error: %s : %s", err->message, debug);
|
||||||
|
g_error_free (err);
|
||||||
|
g_free (debug);
|
||||||
|
|
||||||
|
gtk_main_quit ();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GST_MESSAGE_EOS:
|
||||||
|
{
|
||||||
|
GST_INFO ("EOS");
|
||||||
|
gtk_main_quit ();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GST_MESSAGE_STATE_CHANGED:
|
||||||
|
{
|
||||||
|
GstState old_state, new_state;
|
||||||
|
gst_message_parse_state_changed (message, &old_state, &new_state, NULL);
|
||||||
|
GstStateChange transition = (GstStateChange)GST_STATE_TRANSITION (old_state, new_state);
|
||||||
|
GstElement *src = GST_ELEMENT (GST_MESSAGE_SRC (message));
|
||||||
|
GST_DEBUG ("%" GST_PTR_FORMAT " state transition %s -> %s", src, gst_element_state_get_name(GST_STATE_TRANSITION_CURRENT(transition)), gst_element_state_get_name(GST_STATE_TRANSITION_NEXT(transition)));
|
||||||
|
if (src == pb->video_bin && transition == GST_STATE_CHANGE_PAUSED_TO_PLAYING)
|
||||||
|
{
|
||||||
|
GST_DEBUG ("video_bin GST_STATE_CHANGE_READY_TO_PAUSED -> RUN CAPTURE!");
|
||||||
|
SEND_COMMAND (pb, CONTROL_RUN);
|
||||||
|
}
|
||||||
|
if (src == pb->video_bin && transition == GST_STATE_CHANGE_PLAYING_TO_PAUSED)
|
||||||
|
{
|
||||||
|
GST_DEBUG ("video_bin GST_STATE_CHANGE_PLAYING_TO_PAUSED -> PAUSE CAPTURE!");
|
||||||
|
SEND_COMMAND (pb, CONTROL_PAUSE);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
// GST_DEBUG ("gst_message from %" GST_PTR_FORMAT ": %" GST_PTR_FORMAT "", GST_MESSAGE_SRC(message), message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern int camera_auto_focus (Camera *list, GPContext *context, int onoff);
|
||||||
|
|
||||||
|
static gboolean photo_booth_focus (CameraInfo *cam_info)
|
||||||
|
{
|
||||||
|
int gpret;
|
||||||
|
// CameraEventType evttype;
|
||||||
|
// void *evtdata;
|
||||||
|
|
||||||
|
|
||||||
|
// do {
|
||||||
|
// g_mutex_lock (&cam_info->mutex);
|
||||||
|
// gpret = gp_camera_wait_for_event (cam_info->camera, 10, &evttype, &evtdata, cam_info->context);
|
||||||
|
// g_mutex_unlock (&cam_info->mutex);
|
||||||
|
// GST_DEBUG ("gp_camera_wait_for_event gpret=%i", gpret);
|
||||||
|
// } while ((gpret == GP_OK) && (evttype != GP_EVENT_TIMEOUT));
|
||||||
|
//
|
||||||
|
// g_mutex_lock (&cam_info->mutex);
|
||||||
|
// gpret = camera_auto_focus (cam_info->camera, cam_info->context, 1);
|
||||||
|
// g_mutex_unlock (&cam_info->mutex);
|
||||||
|
// if (gpret != GP_OK) {
|
||||||
|
// GST_WARNING ("gphoto error: %s\n", gp_result_as_string(gpret));
|
||||||
|
// return FALSE;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// do {
|
||||||
|
// GST_DEBUG ("gp_camera_wait_for_event gpret=%i", gpret);
|
||||||
|
// g_mutex_lock (&cam_info->mutex);
|
||||||
|
// gpret = gp_camera_wait_for_event (cam_info->camera, 10, &evttype, &evtdata, cam_info->context);
|
||||||
|
// g_mutex_unlock (&cam_info->mutex);
|
||||||
|
// } while ((gpret == GP_OK) && (evttype != GP_EVENT_TIMEOUT));
|
||||||
|
//
|
||||||
|
g_mutex_lock (&cam_info->mutex);
|
||||||
|
gpret = camera_auto_focus (cam_info->camera, cam_info->context, 0);
|
||||||
|
g_mutex_unlock (&cam_info->mutex);
|
||||||
|
if (gpret != GP_OK) {
|
||||||
|
GST_WARNING ("gphoto error: %s\n", gp_result_as_string(gpret));
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean photo_booth_take_photo (CameraInfo *cam_info, const char **ptr, unsigned long int *size)
|
||||||
|
{
|
||||||
|
int gpret;
|
||||||
|
CameraFile *file;
|
||||||
|
CameraFilePath camera_file_path;
|
||||||
|
|
||||||
|
g_mutex_lock (&cam_info->mutex);
|
||||||
|
strcpy (camera_file_path.folder, "/");
|
||||||
|
strcpy (camera_file_path.name, "foo.jpg");
|
||||||
|
// snprintf (camera_file_path.name, 128, "pb_capt_%04d", cam_info->preview_capture_count);
|
||||||
|
gpret = gp_camera_capture (cam_info->camera, GP_CAPTURE_IMAGE, &camera_file_path, cam_info->context);
|
||||||
|
GST_DEBUG ("gp_camera_capture gpret=%i Pathname on the camera: %s/%s", gpret, camera_file_path.folder, camera_file_path.name);
|
||||||
|
if (gpret < 0)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
gpret = gp_file_new (&file);
|
||||||
|
GST_DEBUG ("gp_file_new gpret=%i", gpret);
|
||||||
|
|
||||||
|
gpret = gp_camera_file_get (cam_info->camera, camera_file_path.folder, camera_file_path.name, GP_FILE_TYPE_NORMAL, file, cam_info->context);
|
||||||
|
GST_DEBUG ("gp_camera_file_get gpret=%i", gpret);
|
||||||
|
if (gpret < 0)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
gp_file_get_data_and_size (file, ptr, size);
|
||||||
|
if (gpret < 0)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
gpret = gp_camera_file_delete (cam_info->camera, camera_file_path.folder, camera_file_path.name, cam_info->context);
|
||||||
|
GST_DEBUG ("gp_camera_file_delete gpret=%i", gpret);
|
||||||
|
// gp_file_free(file);
|
||||||
|
g_mutex_unlock (&cam_info->mutex);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void photo_booth_clicked (GtkWidget *button, GdkEventButton *event, PhotoBooth *pb)
|
||||||
|
{
|
||||||
|
GST_DEBUG_OBJECT (pb, "photo_booth_clicked state=%d", pb->state);
|
||||||
|
switch (pb->state) {
|
||||||
|
case PB_STATE_PREVIEW:
|
||||||
|
photo_booth_snapshot (pb);
|
||||||
|
break;
|
||||||
|
case PB_STATE_TAKING_PHOTO:
|
||||||
|
GST_WARNING_OBJECT (pb, "BUSY TAKING A PHOTO, IGNORE CLICK");
|
||||||
|
break;
|
||||||
|
case PB_STATE_ASKING:
|
||||||
|
photo_booth_preview (pb);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void photo_booth_preview (PhotoBooth *pb)
|
||||||
|
{
|
||||||
|
GstPad *pad;
|
||||||
|
if (pb->video_block_id)
|
||||||
|
{
|
||||||
|
GST_DEBUG_OBJECT (pb, "photo_booth_preview! halt photo_bin...");
|
||||||
|
gst_element_set_state (pb->photo_bin, GST_STATE_PAUSED);
|
||||||
|
pad = gst_element_get_static_pad (pb->photo_bin, "src");
|
||||||
|
pb->photo_block_id = gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM, _gst_photo_probecb, pb, NULL);
|
||||||
|
gst_object_unref (pad);
|
||||||
|
gst_element_unlink (pb->photo_bin, pb->pixoverlay);
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (pb, "photo_booth_preview! unblock video_bin...");
|
||||||
|
pad = gst_element_get_static_pad (pb->video_bin, "src");
|
||||||
|
gst_pad_remove_probe (pad, pb->video_block_id);
|
||||||
|
gst_object_unref (pad);
|
||||||
|
}
|
||||||
|
int ret = gst_element_link (pb->video_bin, pb->pixoverlay);
|
||||||
|
GST_DEBUG_OBJECT (pb, "linking %" GST_PTR_FORMAT " ! %" GST_PTR_FORMAT " ret=%i", pb->video_bin, pb->pixoverlay, ret);
|
||||||
|
gst_element_set_state (pb->video_bin, GST_STATE_PLAYING);
|
||||||
|
// SEND_COMMAND (pb, CONTROL_RUN);
|
||||||
|
GST_DEBUG_OBJECT (pb, "photo_booth_preview done");
|
||||||
|
pb->state = PB_STATE_PREVIEW;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void photo_booth_snapshot (PhotoBooth *pb)
|
||||||
|
{
|
||||||
|
GstPad *pad;
|
||||||
|
gboolean ret;
|
||||||
|
|
||||||
|
pb->state = PB_STATE_TAKING_PHOTO;
|
||||||
|
|
||||||
|
GST_INFO_OBJECT (pb, "SNAPSHOT!");
|
||||||
|
|
||||||
|
if (!photo_booth_focus (pb->cam_info))
|
||||||
|
{
|
||||||
|
GST_WARNING_OBJECT (pb, "OUT OF FOCUS!");
|
||||||
|
pb->state = PB_STATE_PREVIEW;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gst_element_set_state (pb->video_bin, GST_STATE_READY);
|
||||||
|
GST_DEBUG_OBJECT (pb, "photo_booth_preview! halt video_bin...");
|
||||||
|
pad = gst_element_get_static_pad (pb->video_bin, "src");
|
||||||
|
pb->video_block_id = gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM, _gst_video_probecb, pb, NULL);
|
||||||
|
gst_object_unref (pad);
|
||||||
|
gst_element_unlink (pb->video_bin, pb->pixoverlay);
|
||||||
|
|
||||||
|
if (pb->photo_block_id)
|
||||||
|
{
|
||||||
|
GST_DEBUG_OBJECT (pb, "photo_booth_preview! unblock photo_bin...");
|
||||||
|
pad = gst_element_get_static_pad (pb->photo_bin, "src");
|
||||||
|
gst_pad_remove_probe (pad, pb->photo_block_id);
|
||||||
|
gst_object_unref (pad);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = gst_element_link (pb->photo_bin, pb->pixoverlay);
|
||||||
|
GST_DEBUG_OBJECT (pb, "linking %" GST_PTR_FORMAT " ! %" GST_PTR_FORMAT " ret=%i", pb->photo_bin, pb->pixoverlay, ret);
|
||||||
|
gst_element_set_state (pb->photo_bin, GST_STATE_PLAYING);
|
||||||
|
|
||||||
|
char *data;
|
||||||
|
unsigned long size;
|
||||||
|
ret = photo_booth_take_photo (pb->cam_info, (const char**)&data, &size);
|
||||||
|
if (ret)
|
||||||
|
{
|
||||||
|
GstElement *appsrc = gst_bin_get_by_name (GST_BIN (pb->photo_bin), "photo-appsrc");
|
||||||
|
GstBuffer *buffer;
|
||||||
|
GstFlowReturn flowret;
|
||||||
|
|
||||||
|
buffer = gst_buffer_new_wrapped (data, size);
|
||||||
|
g_signal_emit_by_name (appsrc, "push-buffer", buffer, &flowret);
|
||||||
|
|
||||||
|
if (flowret != GST_FLOW_OK)
|
||||||
|
GST_ERROR_OBJECT(appsrc, "couldn't push %" GST_PTR_FORMAT " to appsrc", buffer);
|
||||||
|
gst_object_unref (appsrc);
|
||||||
|
GST_INFO_OBJECT (pb, "photo_booth_snapshot now waiting for user input... PB_STATE_ASKING");
|
||||||
|
pb->state = PB_STATE_ASKING;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
GST_INFO_OBJECT (pb, "photo_booth_take_photo_push_to_appsrc failed!");
|
||||||
|
gst_element_set_state (pb->pipeline, GST_STATE_NULL);
|
||||||
|
g_main_loop_quit (pb->loop);
|
||||||
|
// photo_booth_preview (pb);
|
||||||
|
}
|
||||||
|
|
||||||
|
PhotoBooth *photo_booth_new (void)
|
||||||
|
{
|
||||||
|
return g_object_new (PHOTO_BOOTH_TYPE,
|
||||||
|
"application-id", "org.schaffenburg.photobooth",
|
||||||
|
"flags", G_APPLICATION_HANDLES_OPEN,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (PhotoBoothWindow, photo_booth_window, GTK_TYPE_APPLICATION_WINDOW);
|
||||||
|
|
||||||
|
static void photo_booth_window_init (PhotoBoothWindow *win)
|
||||||
|
{
|
||||||
|
gtk_window_set_title (GTK_WINDOW (win), "Live Video Preview");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void photo_booth_window_class_init (PhotoBoothWindowClass *class)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
PhotoBoothWindow * photo_booth_window_new (PhotoBooth *pb)
|
||||||
|
{
|
||||||
|
g_print ("photo_booth_window_new\n");
|
||||||
|
return g_object_new (PHOTO_BOOTH_WINDOW_TYPE, "photobooth", pb, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void photo_booth_window_open (PhotoBoothWindow *win, GFile *file)
|
||||||
|
{
|
||||||
|
g_print ("photo_booth_window_open\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main (int argc, char *argv[])
|
||||||
|
{
|
||||||
|
PhotoBooth *pb;
|
||||||
|
|
||||||
|
gst_init (&argc, &argv);
|
||||||
|
|
||||||
|
pb = photo_booth_new ();
|
||||||
|
pb->loop = g_main_loop_new (NULL, FALSE);
|
||||||
|
|
||||||
|
g_unix_signal_add (SIGINT, (GSourceFunc) photo_booth_quit_signal, pb->loop);
|
||||||
|
g_application_run (G_APPLICATION (pb), argc, argv);
|
||||||
|
g_main_loop_run (pb->loop);
|
||||||
|
|
||||||
|
g_object_unref (pb);
|
||||||
|
return 0;
|
||||||
|
|
||||||
g_main_loop_run (loop);
|
|
||||||
|
|
||||||
g_object_unref (pb);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
74
photobooth.h
74
photobooth.h
|
@ -16,13 +16,15 @@
|
||||||
#ifndef __PHOTO_BOOTH_H__
|
#ifndef __PHOTO_BOOTH_H__
|
||||||
#define __PHOTO_BOOTH_H__
|
#define __PHOTO_BOOTH_H__
|
||||||
|
|
||||||
|
|
||||||
#include <glib-unix.h>
|
#include <glib-unix.h>
|
||||||
#include <glib-object.h>
|
#include <glib-object.h>
|
||||||
|
#include <gtk/gtk.h>
|
||||||
#include <gst/gst.h>
|
#include <gst/gst.h>
|
||||||
#include <gphoto2/gphoto2.h>
|
#include <gphoto2/gphoto2.h>
|
||||||
|
#include <gphoto2/gphoto2-camera.h>
|
||||||
|
|
||||||
#define CONTROL_RUN 'R' /* start movie capture */
|
#define CONTROL_RUN 'R' /* start movie capture */
|
||||||
|
#define CONTROL_PAUSE 'P' /* pause movie capture */
|
||||||
#define CONTROL_STOP 'S' /* stop movie capture */
|
#define CONTROL_STOP 'S' /* stop movie capture */
|
||||||
#define CONTROL_SOCKETS(src) src->control_sock
|
#define CONTROL_SOCKETS(src) src->control_sock
|
||||||
#define WRITE_SOCKET(src) src->control_sock[1]
|
#define WRITE_SOCKET(src) src->control_sock[1]
|
||||||
|
@ -47,56 +49,98 @@ G_STMT_START { \
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
#define PREVIEW_FPS 25
|
#define PREVIEW_FPS 24
|
||||||
|
|
||||||
struct _CameraInfo {
|
struct _CameraInfo {
|
||||||
Camera *camera;
|
Camera *camera;
|
||||||
GPContext *context;
|
GPContext *context;
|
||||||
|
GMutex mutex;
|
||||||
|
int preview_capture_count;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
CAPTURETHREAD_NONE = 0,
|
CAPTURETHREAD_NONE = 0,
|
||||||
|
CAPTURETHREAD_STOP,
|
||||||
|
CAPTURETHREAD_PAUSED,
|
||||||
CAPTURETHREAD_RUN,
|
CAPTURETHREAD_RUN,
|
||||||
CAPTURETHREAD_STOP
|
|
||||||
} PhotoboothReadthreadState;
|
} PhotoboothReadthreadState;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
PB_STATE_NONE = 0,
|
||||||
|
PB_STATE_PREVIEW,
|
||||||
|
PB_STATE_TAKING_PHOTO,
|
||||||
|
PB_STATE_ASKING,
|
||||||
|
PB_STATE_PRINTING
|
||||||
|
} PhotoboothState;
|
||||||
|
|
||||||
#define PHOTO_BOOTH_TYPE (photo_booth_get_type ())
|
#define PHOTO_BOOTH_TYPE (photo_booth_get_type ())
|
||||||
#define PHOTO_BOOTH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),PHOTO_BOOTH_TYPE,PhotoBooth))
|
#define PHOTO_BOOTH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),PHOTO_BOOTH_TYPE,PhotoBooth))
|
||||||
#define PHOTO_BOOTH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PHOTO_BOOTH_TYPE,PhotoBoothClass))
|
#define PHOTO_BOOTH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PHOTO_BOOTH_TYPE,PhotoBoothClass))
|
||||||
#define IS_PHOTO_BOOTH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),PHOTO_BOOTH_TYPE))
|
#define IS_PHOTO_BOOTH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),PHOTO_BOOTH_TYPE))
|
||||||
#define IS_PHOTO_BOOTH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PHOTO_BOOTH_TYPE))
|
#define IS_PHOTO_BOOTH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PHOTO_BOOTH_TYPE))
|
||||||
|
|
||||||
typedef struct _PhotoBooth PhotoBooth;
|
typedef struct _PhotoBooth PhotoBooth;
|
||||||
typedef struct _PhotoBoothClass PhotoBoothClass;
|
typedef struct _PhotoBoothClass PhotoBoothClass;
|
||||||
|
|
||||||
typedef struct _CameraInfo CameraInfo;
|
typedef struct _CameraInfo CameraInfo;
|
||||||
|
|
||||||
struct _PhotoBooth
|
struct _PhotoBooth
|
||||||
{
|
{
|
||||||
|
GtkApplication parent;
|
||||||
GMainLoop *loop;
|
GMainLoop *loop;
|
||||||
|
|
||||||
GstElement *pipeline;
|
GstElement *pipeline;
|
||||||
GstElement *mjpeg_source, *mjpeg_decoder, *mjpeg_filter, *video_filter, *video_scale, *video_convert;
|
|
||||||
GstElement *photo_source, *photo_decoder, *photo_freeze, *photo_scale, *photo_filter;
|
|
||||||
GstElement *pixoverlay, *video_sink;
|
GstElement *pixoverlay, *video_sink;
|
||||||
|
GstElement *video_bin;
|
||||||
|
GstElement *photo_bin;
|
||||||
|
|
||||||
int video_fd;
|
int video_fd;
|
||||||
gint timeout_id;
|
gint timeout_id;
|
||||||
GPid pid;
|
GtkWidget *overlay;
|
||||||
GtkWidget *window, *overlay;
|
|
||||||
GtkWidget *drawing_area, *text;
|
GtkWidget *drawing_area, *text;
|
||||||
GdkRectangle monitor_geo;
|
GdkRectangle monitor_geo;
|
||||||
CameraInfo cam_info;
|
CameraInfo *cam_info;
|
||||||
|
|
||||||
|
gulong video_block_id;
|
||||||
|
gulong photo_block_id;
|
||||||
|
|
||||||
int control_sock[2];
|
int control_sock[2];
|
||||||
GThread *video_capture_thread;
|
GThread *video_capture_thread;
|
||||||
|
PhotoboothState state;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _PhotoBoothClass
|
struct _PhotoBoothClass
|
||||||
{
|
{
|
||||||
GObjectClass parent_class;
|
GtkApplicationClass parent_class;
|
||||||
};
|
};
|
||||||
|
|
||||||
GType photo_booth_get_type (void);
|
GType photo_booth_get_type (void);
|
||||||
|
|
||||||
|
|
||||||
|
#define PHOTO_BOOTH_WINDOW_TYPE (photo_booth_window_get_type ())
|
||||||
|
#define PHOTO_BOOTH_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),PHOTO_BOOTH_WINDOW_TYPE,PhotoBoothWindow))
|
||||||
|
#define PHOTO_BOOTH_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PHOTO_BOOTH_WINDOW_TYPE,PhotoBoothWindowClass))
|
||||||
|
#define IS_PHOTO_BOOTH_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),PHOTO_BOOTH_WINDOW_TYPE))
|
||||||
|
#define IS_PHOTO_BOOTH_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PHOTO_BOOTH_WINDOW_TYPE))
|
||||||
|
|
||||||
|
typedef struct _PhotoBoothWindow PhotoBoothWindow;
|
||||||
|
typedef struct _PhotoBoothWindowClass PhotoBoothWindowClass;
|
||||||
|
|
||||||
|
struct _PhotoBoothWindow
|
||||||
|
{
|
||||||
|
GtkApplicationWindow parent;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _PhotoBoothWindowClass
|
||||||
|
{
|
||||||
|
GtkApplicationWindowClass parent_class;
|
||||||
|
};
|
||||||
|
|
||||||
|
GType photo_booth_window_get_type (void);
|
||||||
|
PhotoBoothWindow *photo_booth_window_new (PhotoBooth *pb);
|
||||||
|
void photo_booth_window_open (PhotoBoothWindow *win, GFile *file);
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue