eb71b7eba9a09c7bc98af2ba0753928b9ac29468
[easyphoto] / camera.c
1 /**
2  * This file is part of maemo-examples package
3  *
4  * Copyright (c) 2007-2008 Nokia Corporation. All rights reserved.
5  * Copyright (c) 2006 INdT.
6  * @author Talita Menezes <talita.menezes@indt.org.br>
7  * @author Cidorvan Leite <cidorvan.leite@indt.org.br>
8  * @author Jami Pekkanen <jami.pekkanen@nokia.com>
9  * @author Rouslan V. Solomakhin <rouslan@solomakhin.net>
10  *
11  * This maemo code example is licensed under a MIT-style license.
12  */
13
14 #include <gtk/gtk.h>
15 #include "camera.h"
16
17 #define VIDEO_SRC "v4l2src"
18
19 static gboolean create_jpeg(unsigned char *buffer, const char *file_name);
20
21 /* This callback will be registered to the image sink
22  * after user requests a photo */
23 static gboolean buffer_probe_callback(GstElement *image_sink,
24     GstBuffer *buffer, GstPad *pad, photo_t *appdata)
25 {
26         /* This is the raw RGB-data that image sink is about
27          * to discard */
28         unsigned char *data_photo = (unsigned char *) GST_BUFFER_DATA(buffer);
29
30         /* Create a JPEG of the data and check the status */
31         if(!create_jpeg(data_photo, appdata->file_name))
32                 appdata->success = FALSE;
33         else
34                 appdata->success = TRUE;
35
36         /* Disconnect the handler so no more photos
37          * are taken */
38         g_signal_handler_disconnect(G_OBJECT(image_sink), appdata->buffer_cb_id);
39
40   pthread_mutex_unlock( &(appdata->lock) );
41
42   return TRUE;
43 }
44
45 /* Callback that gets called when user clicks the "Take photo" button */
46 static void take_photo(photo_t *appdata) {
47         GstElement *image_sink;
48
49         /* Get the image sink element from the pipeline */
50         image_sink = gst_bin_get_by_name(GST_BIN(appdata->pipeline), "image_sink");
51
52         /* Connect the "handoff"-signal of the image sink to the
53          * callback. This gets called whenever the sink gets a
54          * buffer it's ready to pass forward on the pipeline */
55         appdata->buffer_cb_id = g_signal_connect(G_OBJECT(image_sink), "handoff", G_CALLBACK(buffer_probe_callback), appdata);
56 }
57
58 /* Initialize the the Gstreamer pipeline. Below is a diagram
59  * of the pipeline that will be created:
60  *
61  * |Camera|  |CSP   |
62  * |src   |->|Filter|->|Image|   |Image |  |Image|
63  *                     |queue|-> |filter|->|sink |-> JPEG file
64  */
65 static gboolean initialize_pipeline(photo_t *appdata, int *argc, char ***argv) {
66         GstElement *pipeline, *camera_src, *image_sink;
67         GstElement *image_queue;
68         GstElement *csp_filter, *image_filter;
69         GstCaps *caps;
70
71         /* Initialize Gstreamer */
72         gst_init(argc, argv);
73
74         /* Create pipeline and attach a callback to it's
75          * message bus */
76         pipeline = gst_pipeline_new("test-camera");
77
78         /* Save pipeline to the photo_t structure */
79         appdata->pipeline = pipeline;
80
81         /* Create elements */
82         /* Camera video stream comes from a Video4Linux driver */
83         camera_src = gst_element_factory_make(VIDEO_SRC, "camera_src");
84         /* Colorspace filter is needed to make sure that sinks understands
85          * the stream coming from the camera */
86         csp_filter = gst_element_factory_make("ffmpegcolorspace", "csp_filter");
87         /* Creates separate thread for the stream from which the image
88          * is captured */
89         image_queue = gst_element_factory_make("queue", "image_queue");
90         /* Filter to convert stream to use format that the gdkpixbuf library
91          * can use */
92         image_filter = gst_element_factory_make("ffmpegcolorspace", "image_filter");
93         /* A dummy sink for the image stream. Goes to bitheaven */
94         image_sink = gst_element_factory_make("fakesink", "image_sink");
95
96         /* Check that elements are correctly initialized */
97         if (!(pipeline && camera_src && csp_filter && image_queue && image_filter && image_sink)) {
98                 g_critical("Couldn't create pipeline elements");
99                 return FALSE;
100         }
101
102         /* Set image sink to emit handoff-signal before throwing away
103          * it's buffer */
104         g_object_set(G_OBJECT(image_sink), "signal-handoffs", TRUE, NULL);
105
106         /* Add elements to the pipeline. This has to be done prior to
107          * linking them */
108         gst_bin_add_many(GST_BIN(pipeline), camera_src, csp_filter, image_queue, image_filter, image_sink, NULL);
109
110         /* Specify what kind of video is wanted from the camera */
111         caps = gst_caps_new_simple("video/x-raw-rgb",
112                         "width", G_TYPE_INT, 640,
113                         "height", G_TYPE_INT, 480,
114                         NULL);
115
116         /* Link the camera source and colorspace filter using capabilities
117          * specified */
118         if (!gst_element_link_filtered(camera_src, csp_filter, caps)) {
119                 return FALSE;
120         }
121         gst_caps_unref(caps);
122
123         /* gdkpixbuf requires 8 bits per sample which is 24 bits per
124          * pixel */
125         caps = gst_caps_new_simple("video/x-raw-rgb",
126                         "width", G_TYPE_INT, 640,
127                         "height", G_TYPE_INT, 480,
128                         "bpp", G_TYPE_INT, 24,
129                         "depth", G_TYPE_INT, 24,
130                         "framerate", GST_TYPE_FRACTION, 15, 1,
131                         NULL);
132
133         /* Link the image-branch of the pipeline. The pipeline is
134          * ready after this */
135         if(!gst_element_link_many(csp_filter, image_queue, image_filter, NULL)) return FALSE;
136         if(!gst_element_link_filtered(image_filter, image_sink, caps)) return FALSE;
137
138         gst_caps_unref(caps);
139
140         gst_element_set_state(pipeline, GST_STATE_PLAYING);
141
142         return TRUE;
143 }
144
145 /* Destroy the pipeline on exit */
146 static void destroy_pipeline(photo_t *appdata) {
147         /* Free the pipeline. This automatically also unrefs all elements
148          * added to the pipeline */
149         gst_element_set_state(appdata->pipeline, GST_STATE_NULL);
150         gst_object_unref(GST_OBJECT(appdata->pipeline));
151 }
152
153 gboolean photo_initialize( photo_t *context, int *argc, char **argv[] )
154 {
155   pthread_mutex_init( &(context->lock), NULL );
156   
157   memset( context->file_name, 0, PHOTO_FILE_NAME_LENGTH );
158   context->success = TRUE;
159   
160         /* Initialize the GST pipeline */
161         if (!initialize_pipeline(context, argc, argv)) {
162                 g_critical("Failed to initialize pipeline");
163     return FALSE;
164         }
165
166   return TRUE;
167 }
168
169 gboolean photo_take( photo_t *context, const char *file_name )
170 {
171   pthread_mutex_lock( &(context->lock) );
172   
173   strncpy( context->file_name, file_name, PHOTO_FILE_NAME_LENGTH );
174   
175   take_photo( context );
176   
177   pthread_mutex_lock( &(context->lock) );
178   pthread_mutex_unlock( &(context->lock) );
179   
180   return context->success;
181 }
182
183 gboolean photo_destroy( photo_t *context )
184 {
185         /* Free the gstreamer resources. Elements added
186          * to the pipeline will be freed automatically */
187   destroy_pipeline(context);
188
189   pthread_mutex_unlock( &(context->lock) );
190   pthread_mutex_destroy( &(context->lock) );
191
192         return TRUE;
193 }
194
195 /* Creates a jpeg file from the buffer's raw image data */
196 static gboolean create_jpeg(unsigned char *data, const char *file_name)
197 {
198         GdkPixbuf *pixbuf = NULL;
199         GError *error = NULL;
200         guint height, width, bpp;
201
202         width = 640; height = 480; bpp = 24;
203
204         /* Create a pixbuf object from the data */
205         pixbuf = gdk_pixbuf_new_from_data(data,
206                         GDK_COLORSPACE_RGB, /* RGB-colorspace */
207                         FALSE, /* No alpha-channel */
208                         bpp/3, /* Bits per RGB-component */
209                         width, height, /* Dimensions */
210                         3*width, /* Number of bytes between lines (ie stride) */
211                         NULL, NULL); /* Callbacks */
212
213         /* Save the pixbuf content's in to a jpeg file and check for
214          * errors */
215         if (!gdk_pixbuf_save(pixbuf, file_name, "jpeg", &error, NULL)) {
216                 g_warning("%s\n", error->message);
217                 g_error_free(error);
218                 gdk_pixbuf_unref(pixbuf);
219                 return FALSE;
220         }
221
222         /* Free allocated resources and return TRUE which means
223          * that the operation was succesful */
224         gdk_pixbuf_unref(pixbuf);
225         return TRUE;
226 }