230 lines
6.1 KiB
C
230 lines
6.1 KiB
C
/*
|
|
* GStreamer photobooth.c
|
|
* Copyright 2016 Andreas Frisch <fraxinas@opendreambox.org>
|
|
*
|
|
* This program is licensed under the Creative Commons
|
|
* Attribution-NonCommercial-ShareAlike 3.0 Unported
|
|
* License. To view a copy of this license, visit
|
|
* http://creativecommons.org/licenses/by-nc-sa/3.0/ or send a letter to
|
|
* Creative Commons,559 Nathan Abbott Way,Stanford,California 94305,USA.
|
|
*
|
|
* This program is NOT free software. It is open source, you are allowed
|
|
* to modify it (if you keep the license), but it may not be commercially
|
|
* 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
|
|
|
|
#include <poll.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <fcntl.h>
|
|
#include <signal.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/un.h>
|
|
#include <gtk/gtk.h>
|
|
#include <gdk/gdkx.h>
|
|
#include <gst/video/videooverlay.h>
|
|
#include "photobooth.h"
|
|
|
|
#define photo_booth_parent_class parent_class
|
|
G_DEFINE_TYPE (PhotoBooth, photo_booth, G_TYPE_OBJECT);
|
|
|
|
GST_DEBUG_CATEGORY_STATIC (photo_booth_debug);
|
|
#define GST_CAT_DEFAULT photo_booth_debug
|
|
|
|
gboolean _pb_cam_init (CameraInfo *cam_info);
|
|
gboolean _pb_cam_close (CameraInfo *cam_info);
|
|
void _pb_flush_pipe (int fd);
|
|
static void _pb_video_capture_thread_func (PhotoBooth *pb);
|
|
|
|
static void photo_booth_finalize (GObject * object);
|
|
|
|
static void photo_booth_class_init (PhotoBoothClass *klass)
|
|
{
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
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");
|
|
gobject_class->finalize = photo_booth_finalize;
|
|
}
|
|
|
|
static void photo_booth_init (PhotoBooth *pb)
|
|
{
|
|
GST_DEBUG_OBJECT (pb, "photo_booth_init init object!");
|
|
|
|
int control_sock[2];
|
|
if (socketpair (PF_UNIX, SOCK_STREAM, 0, control_sock) < 0)
|
|
{
|
|
GST_ERROR_OBJECT (pb, "cannot create control sockets: %s (%i)", strerror(errno), errno);
|
|
return;
|
|
}
|
|
READ_SOCKET (pb) = control_sock[0];
|
|
WRITE_SOCKET (pb) = control_sock[1];
|
|
fcntl (READ_SOCKET (pb), F_SETFL, O_NONBLOCK);
|
|
fcntl (WRITE_SOCKET (pb), F_SETFL, O_NONBLOCK);
|
|
|
|
if (!_pb_cam_init (&pb->cam_info))
|
|
g_error ("can't init cam!");
|
|
|
|
pb->video_capture_thread = NULL;
|
|
pb->video_capture_thread = g_thread_try_new ("video-capture", (GThreadFunc) _pb_video_capture_thread_func, pb, NULL);
|
|
}
|
|
|
|
static void photo_booth_finalize (GObject * object)
|
|
{
|
|
PhotoBooth *pb = PHOTO_BOOTH (object);
|
|
GST_INFO_OBJECT (pb, "finalize server");
|
|
SEND_COMMAND (pb, CONTROL_STOP);
|
|
_pb_flush_pipe (pb->video_fd);
|
|
g_thread_join (pb->video_capture_thread);
|
|
_pb_cam_close (&pb->cam_info);
|
|
}
|
|
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);
|
|
}
|
|
|
|
gboolean _pb_cam_init (CameraInfo *cam_info)
|
|
{
|
|
int retval;
|
|
|
|
cam_info->context = gp_context_new();
|
|
gp_log_add_func(GP_LOG_ERROR, _gphoto_gst_errordumper, NULL);
|
|
gp_camera_new(&cam_info->camera);
|
|
retval = gp_camera_init(cam_info->camera, cam_info->context);
|
|
GST_DEBUG ("gp_camera_init returned %d", retval);
|
|
if (retval != GP_OK) {
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean _pb_cam_close (CameraInfo *cam_info)
|
|
{
|
|
int retval;
|
|
retval = gp_camera_exit(cam_info->camera, cam_info->context);
|
|
GST_DEBUG ("gp_camera_exit returned %i", retval);
|
|
return GP_OK ? TRUE : FALSE;
|
|
}
|
|
|
|
void _pb_flush_pipe (int fd)
|
|
{
|
|
int rlen = 0;
|
|
unsigned char buf[1024];
|
|
fcntl (fd, F_SETFL, O_NONBLOCK);
|
|
while (rlen != -1)
|
|
rlen = read (fd, buf, sizeof(buf));
|
|
}
|
|
void _pb_quit_signal (GMainLoop *loop)
|
|
{
|
|
GST_INFO_OBJECT (loop, "caught SIGINT");
|
|
g_main_loop_quit (loop);
|
|
}
|
|
|
|
static void _pb_video_capture_thread_func (PhotoBooth *pb)
|
|
{
|
|
PhotoboothReadthreadState state = CAPTURETHREAD_NONE;
|
|
|
|
mkfifo("moviepipe", 0666);
|
|
pb->video_fd = open("moviepipe", O_RDWR);
|
|
|
|
GST_DEBUG_OBJECT (pb, "enter video capture thread fd = %d", pb->video_fd);
|
|
|
|
CameraFile *gp_file = NULL;
|
|
int gp_r, captured_frames = 0;
|
|
|
|
if (gp_file_new_from_fd (&gp_file, pb->video_fd) != GP_OK)
|
|
{
|
|
GST_ERROR_OBJECT (pb, "couldn't start video capture thread because gp_file_new_from_fd (%d) failed!", pb->video_fd);
|
|
goto stop_running;
|
|
}
|
|
|
|
while (TRUE) {
|
|
if (state == CAPTURETHREAD_STOP)
|
|
goto stop_running;
|
|
|
|
struct pollfd rfd[2];
|
|
int timeout = 1000 / PREVIEW_FPS;
|
|
rfd[0].fd = READ_SOCKET (pb);
|
|
rfd[0].events = POLLIN | POLLERR | POLLHUP | POLLPRI;
|
|
|
|
int ret = poll(rfd, 1, timeout);
|
|
if (G_UNLIKELY (ret == -1))
|
|
{
|
|
GST_ERROR_OBJECT (pb, "SELECT ERROR!");
|
|
goto stop_running;
|
|
}
|
|
else if ( ret == 0 )
|
|
{
|
|
const char *mime;
|
|
gp_r = gp_camera_capture_preview (pb->cam_info.camera, gp_file, pb->cam_info.context);
|
|
if (gp_r < 0) {
|
|
GST_ERROR_OBJECT (pb, "Movie capture error.");
|
|
state = CAPTURETHREAD_STOP;
|
|
break;
|
|
}
|
|
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 )
|
|
{
|
|
char command;
|
|
READ_COMMAND (pb, command, ret);
|
|
switch (command) {
|
|
case CONTROL_STOP:
|
|
GST_DEBUG_OBJECT (pb, "CONTROL_STOP!");
|
|
state = CAPTURETHREAD_STOP;
|
|
break;
|
|
case CONTROL_RUN:
|
|
GST_DEBUG_OBJECT (pb, "CONTROL_RUN");
|
|
state = CAPTURETHREAD_RUN;
|
|
break;
|
|
default:
|
|
GST_ERROR_OBJECT (pb, "illegal control socket command %c received!", command);
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
|
|
g_assert_not_reached ();
|
|
return;
|
|
|
|
stop_running:
|
|
{
|
|
if (gp_file)
|
|
gp_file_unref (gp_file);
|
|
if (pb->video_fd)
|
|
close (pb->video_fd);
|
|
GST_DEBUG ("stop running, exit thread, %d frames captured", captured_frames);
|
|
return;
|
|
}
|
|
|
|
}
|
|
|
|
int main (int argc, char *argv[])
|
|
{
|
|
GMainLoop *loop;
|
|
PhotoBooth *pb;
|
|
|
|
gst_init (&argc, &argv);
|
|
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);
|
|
|
|
g_main_loop_run (loop);
|
|
|
|
g_object_unref (pb);
|
|
return 0;
|
|
}
|
|
|