2 * This file is part of maemo-examples package
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>
11 * This maemo code example is licensed under a MIT-style license.
17 #define VIDEO_SRC "v4l2src"
19 static gboolean create_jpeg(unsigned char *buffer, const char *file_name);
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)
26 /* This is the raw RGB-data that image sink is about
28 unsigned char *data_photo = (unsigned char *) GST_BUFFER_DATA(buffer);
30 /* Create a JPEG of the data and check the status */
31 if(!create_jpeg(data_photo, appdata->file_name))
32 appdata->success = FALSE;
34 appdata->success = TRUE;
36 /* Disconnect the handler so no more photos
38 g_signal_handler_disconnect(G_OBJECT(image_sink), appdata->buffer_cb_id);
40 pthread_mutex_unlock( &(appdata->lock) );
45 /* Callback that gets called when user clicks the "Take photo" button */
46 static void take_photo(photo_t *appdata) {
47 GstElement *image_sink;
49 /* Get the image sink element from the pipeline */
50 image_sink = gst_bin_get_by_name(GST_BIN(appdata->pipeline), "image_sink");
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);
58 /* Initialize the the Gstreamer pipeline. Below is a diagram
59 * of the pipeline that will be created:
62 * |src |->|Filter|->|Image| |Image | |Image|
63 * |queue|-> |filter|->|sink |-> JPEG file
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;
71 /* Initialize Gstreamer */
74 /* Create pipeline and attach a callback to it's
76 pipeline = gst_pipeline_new("test-camera");
78 /* Save pipeline to the photo_t structure */
79 appdata->pipeline = pipeline;
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
89 image_queue = gst_element_factory_make("queue", "image_queue");
90 /* Filter to convert stream to use format that the gdkpixbuf library
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");
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");
102 /* Set image sink to emit handoff-signal before throwing away
104 g_object_set(G_OBJECT(image_sink), "signal-handoffs", TRUE, NULL);
106 /* Add elements to the pipeline. This has to be done prior to
108 gst_bin_add_many(GST_BIN(pipeline), camera_src, csp_filter, image_queue, image_filter, image_sink, NULL);
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,
116 /* Link the camera source and colorspace filter using capabilities
118 if (!gst_element_link_filtered(camera_src, csp_filter, caps)) {
121 gst_caps_unref(caps);
123 /* gdkpixbuf requires 8 bits per sample which is 24 bits per
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,
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;
138 gst_caps_unref(caps);
140 gst_element_set_state(pipeline, GST_STATE_PLAYING);
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));
153 gboolean photo_initialize( photo_t *context, int *argc, char **argv[] )
155 pthread_mutex_init( &(context->lock), NULL );
157 memset( context->file_name, 0, PHOTO_FILE_NAME_LENGTH );
158 context->success = TRUE;
160 /* Initialize the GST pipeline */
161 if (!initialize_pipeline(context, argc, argv)) {
162 g_critical("Failed to initialize pipeline");
169 gboolean photo_take( photo_t *context, const char *file_name )
171 pthread_mutex_lock( &(context->lock) );
173 strncpy( context->file_name, file_name, PHOTO_FILE_NAME_LENGTH );
175 take_photo( context );
177 pthread_mutex_lock( &(context->lock) );
178 pthread_mutex_unlock( &(context->lock) );
180 return context->success;
183 gboolean photo_destroy( photo_t *context )
185 /* Free the gstreamer resources. Elements added
186 * to the pipeline will be freed automatically */
187 destroy_pipeline(context);
189 pthread_mutex_unlock( &(context->lock) );
190 pthread_mutex_destroy( &(context->lock) );
195 /* Creates a jpeg file from the buffer's raw image data */
196 static gboolean create_jpeg(unsigned char *data, const char *file_name)
198 GdkPixbuf *pixbuf = NULL;
199 GError *error = NULL;
200 guint height, width, bpp;
202 width = 640; height = 480; bpp = 24;
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 */
213 /* Save the pixbuf content's in to a jpeg file and check for
215 if (!gdk_pixbuf_save(pixbuf, file_name, "jpeg", &error, NULL)) {
216 g_warning("%s\n", error->message);
218 gdk_pixbuf_unref(pixbuf);
222 /* Free allocated resources and return TRUE which means
223 * that the operation was succesful */
224 gdk_pixbuf_unref(pixbuf);