move photo capture into thread

This commit is contained in:
Andreas Frisch 2016-04-26 13:27:20 +02:00
parent 1ce8391fa4
commit f54dcb4785
2 changed files with 85 additions and 66 deletions

View file

@ -59,12 +59,14 @@ static void photo_booth_window_destroyed_signal (PhotoBoothWindow *win, GMainLoo
static void photo_booth_setup_window (PhotoBooth *pb); static void photo_booth_setup_window (PhotoBooth *pb);
static void photo_booth_preview (PhotoBooth *pb); static void photo_booth_preview (PhotoBooth *pb);
static void photo_booth_snapshot (PhotoBooth *pb); static void photo_booth_snapshot (PhotoBooth *pb);
static gboolean photo_booth_snapshot_taken (PhotoBooth *pb);
/* libgphoto2 */ /* libgphoto2 */
static gboolean photo_booth_cam_init (CameraInfo **cam_info); static gboolean photo_booth_cam_init (CameraInfo **cam_info);
static gboolean photo_booth_cam_close (CameraInfo **cam_info); static gboolean photo_booth_cam_close (CameraInfo **cam_info);
static gboolean photo_booth_take_photo (CameraInfo *cam_info);
static void photo_booth_flush_pipe (int fd); static void photo_booth_flush_pipe (int fd);
static void photo_booth_video_capture_thread_func (PhotoBooth *pb); static void photo_booth_capture_thread_func (PhotoBooth *pb);
/* gstreamer functions */ /* gstreamer functions */
static gboolean photo_booth_setup_gstreamer (PhotoBooth *pb); static gboolean photo_booth_setup_gstreamer (PhotoBooth *pb);
@ -105,8 +107,8 @@ static void photo_booth_init (PhotoBooth *pb)
pb->video_block_id = 0; pb->video_block_id = 0;
pb->photo_block_id = 0; pb->photo_block_id = 0;
pb->video_capture_thread = NULL; pb->capture_thread = NULL;
pb->video_capture_thread = g_thread_try_new ("video-capture", (GThreadFunc) photo_booth_video_capture_thread_func, pb, NULL); pb->capture_thread = g_thread_try_new ("gphoto-capture", (GThreadFunc) photo_booth_capture_thread_func, pb, NULL);
} }
static void photo_booth_setup_window (PhotoBooth *pb) static void photo_booth_setup_window (PhotoBooth *pb)
@ -141,7 +143,7 @@ static void photo_booth_finalize (GObject *object)
GST_INFO_OBJECT (pb, "finalize server"); GST_INFO_OBJECT (pb, "finalize server");
SEND_COMMAND (pb, CONTROL_STOP); SEND_COMMAND (pb, CONTROL_STOP);
photo_booth_flush_pipe (pb->video_fd); photo_booth_flush_pipe (pb->video_fd);
g_thread_join (pb->video_capture_thread); g_thread_join (pb->capture_thread);
if (pb->cam_info) if (pb->cam_info)
photo_booth_cam_close (&pb->cam_info); photo_booth_cam_close (&pb->cam_info);
} }
@ -183,6 +185,8 @@ static gboolean photo_booth_cam_init (CameraInfo **cam_info)
g_mutex_init (&(*cam_info)->mutex); g_mutex_init (&(*cam_info)->mutex);
g_mutex_lock (&(*cam_info)->mutex); g_mutex_lock (&(*cam_info)->mutex);
(*cam_info)->preview_capture_count = 0; (*cam_info)->preview_capture_count = 0;
(*cam_info)->size = 0;
(*cam_info)->data = NULL;
(*cam_info)->context = gp_context_new(); (*cam_info)->context = gp_context_new();
gp_log_add_func(GP_LOG_ERROR, _gphoto_err, NULL); gp_log_add_func(GP_LOG_ERROR, _gphoto_err, NULL);
gp_camera_new(&(*cam_info)->camera); gp_camera_new(&(*cam_info)->camera);
@ -232,26 +236,26 @@ static void photo_booth_window_destroyed_signal (PhotoBoothWindow *win, GMainLoo
g_main_loop_quit (loop); g_main_loop_quit (loop);
} }
static void photo_booth_video_capture_thread_func (PhotoBooth *pb) static void photo_booth_capture_thread_func (PhotoBooth *pb)
{ {
PhotoboothReadthreadState state = CAPTURETHREAD_NONE; PhotoboothCaptureThreadState state = CAPTURE_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 cam_info@%p", pb->video_fd, pb->cam_info); GST_DEBUG_OBJECT (pb, "enter capture thread fd = %d cam_info@%p", pb->video_fd, pb->cam_info);
CameraFile *gp_file = NULL; CameraFile *gp_file = NULL;
int gpret, 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)
{ {
GST_ERROR_OBJECT (pb, "couldn't start video capture thread because gp_file_new_from_fd (%d) failed!", pb->video_fd); GST_ERROR_OBJECT (pb, "couldn't start capture thread because gp_file_new_from_fd (%d) failed!", pb->video_fd);
goto stop_running; goto stop_running;
} }
while (TRUE) { while (TRUE) {
if (state == CAPTURETHREAD_STOP) if (state == CAPTURE_STOP)
goto stop_running; goto stop_running;
struct pollfd rfd[2]; struct pollfd rfd[2];
@ -267,7 +271,7 @@ static void photo_booth_video_capture_thread_func (PhotoBooth *pb)
} }
else if (pb->state == PB_STATE_NONE) else if (pb->state == PB_STATE_NONE)
pb->state = PB_STATE_PREVIEW; pb->state = PB_STATE_PREVIEW;
else if (state == CAPTURETHREAD_PAUSED) else if (state == CAPTURE_PAUSED)
timeout = 1000; timeout = 1000;
else else
timeout = 1000 / PREVIEW_FPS; timeout = 1000 / PREVIEW_FPS;
@ -278,8 +282,9 @@ static void photo_booth_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 && state >= CAPTURETHREAD_RUN) else if (ret == 0 && state == CAPTURE_VIDEO)
{ {
GST_DEBUG_OBJECT (pb, "STATE == CAPTURE_VIDEO");
const char *mime; const char *mime;
if (pb->cam_info) if (pb->cam_info)
{ {
@ -288,7 +293,7 @@ static void photo_booth_video_capture_thread_func (PhotoBooth *pb)
g_mutex_unlock (&pb->cam_info->mutex); g_mutex_unlock (&pb->cam_info->mutex);
if (gpret < 0) { if (gpret < 0) {
GST_ERROR_OBJECT (pb, "Movie capture error %d", gpret); GST_ERROR_OBJECT (pb, "Movie capture error %d", gpret);
state = CAPTURETHREAD_STOP; state = CAPTURE_STOP;
continue; continue;
} }
else { else {
@ -297,7 +302,7 @@ static void photo_booth_video_capture_thread_func (PhotoBooth *pb)
g_mutex_unlock (&pb->cam_info->mutex); g_mutex_unlock (&pb->cam_info->mutex);
if (strcmp (mime, GP_MIME_JPEG)) { if (strcmp (mime, GP_MIME_JPEG)) {
GST_ERROR_OBJECT ("Movie capture error... Unhandled MIME type '%s'.", mime); GST_ERROR_OBJECT ("Movie capture error... Unhandled MIME type '%s'.", mime);
state = CAPTURETHREAD_STOP; state = CAPTURE_STOP;
continue; continue;
} }
captured_frames++; captured_frames++;
@ -305,6 +310,21 @@ static void photo_booth_video_capture_thread_func (PhotoBooth *pb)
} }
} }
} }
else if (ret == 0 && state == CAPTURE_PHOTO)
{
GST_DEBUG_OBJECT (pb, "STATE == CAPTURE_PHOTO");
if (pb->cam_info)
{
ret = photo_booth_take_photo (pb->cam_info);
if (ret)
g_main_context_invoke (NULL, (GSourceFunc) photo_booth_snapshot_taken, pb);
else
{
GST_ERROR_OBJECT (pb, "taking photo failed!");
state = CAPTURE_STOP;
}
}
}
else if (rfd[0].revents) else if (rfd[0].revents)
{ {
char command; char command;
@ -312,22 +332,27 @@ static void photo_booth_video_capture_thread_func (PhotoBooth *pb)
switch (command) { switch (command) {
case CONTROL_STOP: case CONTROL_STOP:
GST_DEBUG_OBJECT (pb, "CONTROL_STOP!"); GST_DEBUG_OBJECT (pb, "CONTROL_STOP!");
state = CAPTURETHREAD_STOP; state = CAPTURE_STOP;
break; break;
case CONTROL_PAUSE: case CONTROL_PAUSE:
GST_DEBUG_OBJECT (pb, "CONTROL_PAUSE!"); GST_DEBUG_OBJECT (pb, "CONTROL_PAUSE!");
state = CAPTURETHREAD_PAUSED; state = CAPTURE_PAUSED;
break; break;
case CONTROL_RUN: case CONTROL_VIDEO:
GST_DEBUG_OBJECT (pb, "CONTROL_RUN"); GST_DEBUG_OBJECT (pb, "CONTROL_VIDEO");
state = CAPTURETHREAD_RUN; state = CAPTURE_VIDEO;
break;
case CONTROL_PHOTO:
GST_DEBUG_OBJECT (pb, "CONTROL_PHOTO");
state = CAPTURE_PHOTO;
// photo_booth_flush_pipe (pb->video_fd);
break; break;
default: default:
GST_ERROR_OBJECT (pb, "illegal control socket command %c received!", command); GST_ERROR_OBJECT (pb, "illegal control socket command %c received!", command);
} }
continue; continue;
} }
else if (state == CAPTURETHREAD_PAUSED) else if (state == CAPTURE_PAUSED)
{ {
GST_DEBUG_OBJECT (pb, "captured thread paused... timeout"); GST_DEBUG_OBJECT (pb, "captured thread paused... timeout");
} }
@ -345,7 +370,6 @@ static void photo_booth_video_capture_thread_func (PhotoBooth *pb)
GST_DEBUG ("stop running, exit thread, %d frames captured", captured_frames); GST_DEBUG ("stop running, exit thread, %d frames captured", captured_frames);
return; return;
} }
} }
static GstElement *build_video_bin (PhotoBooth *pb) static GstElement *build_video_bin (PhotoBooth *pb)
@ -548,16 +572,16 @@ static gboolean photo_booth_bus_callback (GstBus *bus, GstMessage *message, Phot
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))); 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) if (src == pb->video_bin && transition == GST_STATE_CHANGE_PAUSED_TO_PLAYING)
{ {
GST_DEBUG ("video_bin GST_STATE_CHANGE_READY_TO_PAUSED -> RUN CAPTURE!"); GST_DEBUG ("video_bin GST_STATE_CHANGE_READY_TO_PAUSED -> CAPTURE VIDEO!");
SEND_COMMAND (pb, CONTROL_RUN); SEND_COMMAND (pb, CONTROL_VIDEO);
// priv = photo_booth_get_instance_private (pb); // priv = photo_booth_get_instance_private (pb);
// photo_booth_window_set_spinner (priv->win, FALSE); // photo_booth_window_set_spinner (priv->win, FALSE);
} }
else if (src == pb->video_bin && transition == GST_STATE_CHANGE_PLAYING_TO_PAUSED) // else if (src == pb->video_bin && transition == GST_STATE_CHANGE_PLAYING_TO_PAUSED)
{ // {
GST_DEBUG ("video_bin GST_STATE_CHANGE_PLAYING_TO_PAUSED -> PAUSE CAPTURE!"); // GST_DEBUG ("video_bin GST_STATE_CHANGE_PLAYING_TO_PAUSED -> PAUSE CAPTURE!");
SEND_COMMAND (pb, CONTROL_PAUSE); // SEND_COMMAND (pb, CONTROL_PAUSE);
} // }
break; break;
} }
case GST_MESSAGE_STREAM_START: case GST_MESSAGE_STREAM_START:
@ -617,7 +641,7 @@ static gboolean photo_booth_focus (CameraInfo *cam_info)
return TRUE; return TRUE;
} }
static gboolean photo_booth_take_photo (CameraInfo *cam_info, const char **ptr, unsigned long int *size) static gboolean photo_booth_take_photo (CameraInfo *cam_info)
{ {
int gpret; int gpret;
CameraFile *file; CameraFile *file;
@ -639,8 +663,7 @@ static gboolean photo_booth_take_photo (CameraInfo *cam_info, const char **ptr,
GST_DEBUG ("gp_camera_file_get gpret=%i", gpret); GST_DEBUG ("gp_camera_file_get gpret=%i", gpret);
if (gpret < 0) if (gpret < 0)
return FALSE; return FALSE;
gp_file_get_data_and_size (file, (const char**)&(cam_info->data), &(cam_info->size));
gp_file_get_data_and_size (file, ptr, size);
if (gpret < 0) if (gpret < 0)
return FALSE; return FALSE;
@ -690,7 +713,6 @@ static void photo_booth_preview (PhotoBooth *pb)
int ret = gst_element_link (pb->video_bin, pb->pixoverlay); 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_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); gst_element_set_state (pb->video_bin, GST_STATE_PLAYING);
// SEND_COMMAND (pb, CONTROL_RUN);
GST_DEBUG_OBJECT (pb, "photo_booth_preview done"); GST_DEBUG_OBJECT (pb, "photo_booth_preview done");
pb->state = PB_STATE_PREVIEW; pb->state = PB_STATE_PREVIEW;
} }
@ -710,12 +732,7 @@ static void photo_booth_snapshot (PhotoBooth *pb)
priv = photo_booth_get_instance_private (pb); priv = photo_booth_get_instance_private (pb);
photo_booth_window_set_spinner (priv->win, TRUE); photo_booth_window_set_spinner (priv->win, TRUE);
if (!photo_booth_focus (pb->cam_info)) SEND_COMMAND (pb, CONTROL_PHOTO);
{
GST_WARNING_OBJECT (pb, "OUT OF FOCUS!");
pb->state = PB_STATE_PREVIEW;
return;
}
gst_element_set_state (pb->video_bin, GST_STATE_READY); gst_element_set_state (pb->video_bin, GST_STATE_READY);
GST_DEBUG_OBJECT (pb, "photo_booth_preview! halt video_bin..."); GST_DEBUG_OBJECT (pb, "photo_booth_preview! halt video_bin...");
@ -735,30 +752,28 @@ static void photo_booth_snapshot (PhotoBooth *pb)
ret = gst_element_link (pb->photo_bin, pb->pixoverlay); 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_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); gst_element_set_state (pb->photo_bin, GST_STATE_PLAYING);
}
char *data; static gboolean photo_booth_snapshot_taken (PhotoBooth *pb)
unsigned long size; {
ret = photo_booth_take_photo (pb->cam_info, (const char**)&data, &size); GST_INFO_OBJECT (pb, "photo_booth_snapshot_taken size=%lu", pb->cam_info->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); GstElement *appsrc = gst_bin_get_by_name (GST_BIN (pb->photo_bin), "photo-appsrc");
g_signal_emit_by_name (appsrc, "push-buffer", buffer, &flowret); GstBuffer *buffer;
GstFlowReturn flowret;
if (flowret != GST_FLOW_OK) buffer = gst_buffer_new_wrapped (pb->cam_info->data, pb->cam_info->size);
GST_ERROR_OBJECT(appsrc, "couldn't push %" GST_PTR_FORMAT " to appsrc", buffer); g_signal_emit_by_name (appsrc, "push-buffer", buffer, &flowret);
gst_object_unref (appsrc);
GST_INFO_OBJECT (pb, "photo_booth_snapshot now waiting for user input... PB_STATE_ASKING"); if (flowret != GST_FLOW_OK)
pb->state = PB_STATE_ASKING; GST_ERROR_OBJECT(appsrc, "couldn't push %" GST_PTR_FORMAT " to appsrc", buffer);
return; gst_object_unref (appsrc);
} GST_INFO_OBJECT (pb, "photo_booth_snapshot now waiting for user input... PB_STATE_ASKING");
GST_INFO_OBJECT (pb, "photo_booth_take_photo_push_to_appsrc failed!");
gst_element_set_state (pb->pipeline, GST_STATE_NULL); SEND_COMMAND (pb, CONTROL_PAUSE);
g_main_loop_quit (pb->loop); pb->state = PB_STATE_ASKING;
// photo_booth_preview (pb);
return FALSE; // prevent continously re-running
} }
PhotoBooth *photo_booth_new (void) PhotoBooth *photo_booth_new (void)

View file

@ -23,9 +23,10 @@
#include <gphoto2/gphoto2.h> #include <gphoto2/gphoto2.h>
#include <gphoto2/gphoto2-camera.h> #include <gphoto2/gphoto2-camera.h>
#define CONTROL_RUN 'R' /* start movie capture */ #define CONTROL_VIDEO 'V' /* start movie capture */
#define CONTROL_PAUSE 'P' /* pause movie capture */ #define CONTROL_PHOTO 'T' /* photo capture */
#define CONTROL_STOP 'S' /* stop movie capture */ #define CONTROL_PAUSE 'P' /* pause capture */
#define CONTROL_STOP 'S' /* stop capture thread */
#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]
#define READ_SOCKET(src) src->control_sock[0] #define READ_SOCKET(src) src->control_sock[0]
@ -56,15 +57,18 @@ struct _CameraInfo {
GPContext *context; GPContext *context;
GMutex mutex; GMutex mutex;
int preview_capture_count; int preview_capture_count;
char *data;
unsigned long size;
}; };
typedef enum typedef enum
{ {
CAPTURETHREAD_NONE = 0, CAPTURE_NONE = 0,
CAPTURETHREAD_STOP, CAPTURE_VIDEO,
CAPTURETHREAD_PAUSED, CAPTURE_PHOTO,
CAPTURETHREAD_RUN, CAPTURE_PAUSED,
} PhotoboothReadthreadState; CAPTURE_STOP,
} PhotoboothCaptureThreadState;
typedef enum typedef enum
{ {