02929593154129e75f50a353d560c8fc00e56196
[platform/upstream/gstreamer.git] / ext / opencv / gstmotioncells.c
1 /*
2  * GStreamer MotioCells detect areas of motion
3  * Copyright (C) 2011 Robert Jobbagy <jobbagy.robert@gmail.com>
4  * Copyright (C) 2011 Nicola Murino <nicola.murino@gmail.com>
5  * 
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the "Software"),
8  * to deal in the Software without restriction, including without limitation
9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10  * and/or sell copies of the Software, and to permit persons to whom the
11  * Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22  * DEALINGS IN THE SOFTWARE.
23  *
24  * Alternatively, the contents of this file may be used under the
25  * GNU Lesser General Public License Version 2.1 (the "LGPL"), in
26  * which case the following provisions apply instead of the ones
27  * mentioned above:
28  *
29  * This library is free software; you can redistribute it and/or
30  * modify it under the terms of the GNU Library General Public
31  * License as published by the Free Software Foundation; either
32  * version 2 of the License, or (at your option) any later version.
33  *
34  * This library is distributed in the hope that it will be useful,
35  * but WITHOUT ANY WARRANTY; without even the implied warranty of
36  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
37  * Library General Public License for more details.
38  *
39  * You should have received a copy of the GNU Library General Public
40  * License along with this library; if not, write to the
41  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
42  * Boston, MA 02111-1307, USA.
43  */
44
45 /**
46  * SECTION:element-motioncells
47  *
48  * Performs motion detection on videos.
49  *
50  * <refsect2>
51  * <title>Example launch line</title>
52  * |[
53  * gst-launch-0.10 videotestsrc pattern=18 ! videorate ! videoscale ! video/x-raw-yuv,width=320,height=240,framerate=5/1 ! ffmpegcolorspace ! motioncells ! ffmpegcolorspace ! xvimagesink
54  * ]|
55  * </refsect2>
56  */
57
58 #ifdef HAVE_CONFIG_H
59 #  include <config.h>
60 #endif
61
62 #include <stdio.h>
63 #include <string.h>
64 #include <glib.h>
65 #include "gstmotioncells.h"
66 #include "motioncells_wrapper.h"
67 #include <sys/time.h>
68 #include <time.h>
69 #include <limits.h>
70
71 GST_DEBUG_CATEGORY_STATIC (gst_motion_cells_debug);
72 #define GST_CAT_DEFAULT gst_motion_cells_debug
73
74 #define GRID_DEF 10
75 #define GRID_MIN 8
76 #define GRID_MAX 32
77 #define SENSITIVITY_DEFAULT 0.5
78 #define SENSITIVITY_MIN 0
79 #define SENSITIVITY_MAX 1
80 #define THRESHOLD_MIN 0
81 #define THRESHOLD_DEFAULT 0.01
82 #define THRESHOLD_MAX 1.0
83 #define GAP_MIN 1
84 #define GAP_DEF 5
85 #define GAP_MAX 60
86 #define POST_NO_MOTION_MIN 0
87 #define POST_NO_MOTION_DEF 0
88 #define POST_NO_MOTION_MAX 180
89 #define MINIMUM_MOTION_FRAMES_MIN 1
90 #define MINIMUM_MOTION_FRAMES_DEF 1
91 #define MINIMUM_MOTION_FRAMES_MAX 60
92 #define THICKNESS_MIN -1
93 #define THICKNESS_DEF 1
94 #define THICKNESS_MAX 5
95 #define DATE_MIN 0
96 #define DATE_DEF 1
97 #define DATE_MAX LONG_MAX
98 #define DEF_DATAFILEEXT "vamc"
99 #define MSGLEN 6
100 #define BUSMSGLEN 20
101
102 #define GFREE(POINTER)\
103                 {\
104                         g_free(POINTER);\
105                         POINTER = NULL;\
106                 }
107
108 int instanceCounter = 0;
109 gboolean element_id_was_max = false;
110
111 /* Filter signals and args */
112 enum
113 {
114   /* FILL ME */
115   LAST_SIGNAL
116 };
117
118 enum
119 {
120   PROP_0,
121   PROP_GRID_X,
122   PROP_GRID_Y,
123   PROP_SENSITIVITY,
124   PROP_THRESHOLD,
125   PROP_DISPLAY,
126   PROP_DATE,
127   PROP_DATAFILE,
128   PROP_DATAFILE_EXT,
129   PROP_MOTIONMASKCOORD,
130   PROP_MOTIONMASKCELLSPOS,
131   PROP_CELLSCOLOR,
132   PROP_MOTIONCELLSIDX,
133   PROP_GAP,
134   PROP_POSTNOMOTION,
135   PROP_MINIMUNMOTIONFRAMES,
136   PROP_CALCULATEMOTION,
137   PROP_POSTALLMOTION,
138   PROP_USEALPHA,
139   PROP_MOTIONCELLTHICKNESS
140 };
141
142 /* the capabilities of the inputs and outputs.
143  */
144 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
145     GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS ("video/x-raw-rgb"));
146
147 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
148     GST_PAD_SRC, GST_PAD_ALWAYS, GST_STATIC_CAPS ("video/x-raw-rgb"));
149
150 GST_BOILERPLATE (GstMotioncells, gst_motion_cells, GstElement,
151     GST_TYPE_ELEMENT);
152
153 static void gst_motion_cells_set_property (GObject * object, guint prop_id,
154     const GValue * value, GParamSpec * pspec);
155 static void gst_motion_cells_get_property (GObject * object, guint prop_id,
156     GValue * value, GParamSpec * pspec);
157
158 static gboolean gst_motion_cells_set_caps (GstPad * pad, GstCaps * caps);
159 static GstFlowReturn gst_motion_cells_chain (GstPad * pad, GstBuffer * buf);
160
161 static void gst_motioncells_update_motion_cells (GstMotioncells * filter);
162 static void gst_motioncells_update_motion_masks (GstMotioncells * filter);
163
164 /* Clean up */
165 static void
166 gst_motion_cells_finalize (GObject * obj)
167 {
168   GstMotioncells *filter = gst_motion_cells (obj);
169
170   motion_cells_free (filter->id);
171
172   //freeing previously allocated dynamic array
173   if (filter->motionmaskcoord_count > 0) {
174     GFREE (filter->motionmaskcoords);
175   }
176
177   if (filter->motionmaskcells_count > 0) {
178     GFREE (filter->motionmaskcellsidx);
179   }
180   if (filter->motioncells_count > 0) {
181     GFREE (filter->motioncellsidx);
182   }
183
184   if (filter->cvImage) {
185     cvReleaseImage (&filter->cvImage);
186   }
187
188   GFREE (filter->motioncellscolor);
189   GFREE (filter->prev_datafile);
190   GFREE (filter->cur_datafile);
191   GFREE (filter->basename_datafile);
192   GFREE (filter->datafile_extension);
193
194   g_mutex_free (filter->propset_mutex);
195
196   G_OBJECT_CLASS (parent_class)->finalize (obj);
197 }
198
199 /* GObject vmethod implementations */
200 static void
201 gst_motion_cells_base_init (gpointer gclass)
202 {
203   GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
204
205   gst_element_class_set_details_simple (element_class,
206       "motioncells",
207       "Filter/Effect/Video",
208       "Performs motion detection on videos and images, providing detected motion cells index via bus messages",
209       "Robert Jobbagy <jobbagy dot robert at gmail dot com>, Nicola Murino <nicola dot murino at gmail.com>");
210
211   gst_element_class_add_pad_template (element_class,
212       gst_static_pad_template_get (&src_factory));
213   gst_element_class_add_pad_template (element_class,
214       gst_static_pad_template_get (&sink_factory));
215 }
216
217 /* initialize the motioncells's class */
218 static void
219 gst_motion_cells_class_init (GstMotioncellsClass * klass)
220 {
221   GObjectClass *gobject_class;
222   GstElementClass *gstelement_class;
223
224   gobject_class = (GObjectClass *) klass;
225   gstelement_class = (GstElementClass *) klass;
226   parent_class = g_type_class_peek_parent (klass);
227
228   gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_motion_cells_finalize);
229   gobject_class->set_property = gst_motion_cells_set_property;
230   gobject_class->get_property = gst_motion_cells_get_property;
231
232   g_object_class_install_property (gobject_class, PROP_GRID_X,
233       g_param_spec_int ("gridx", "Number of Horizontal Grids",
234           "You can give number of horizontal grid cells.", GRID_MIN, GRID_MAX,
235           GRID_DEF, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
236   g_object_class_install_property (gobject_class, PROP_GRID_Y,
237       g_param_spec_int ("gridy", "Number of Vertical Grids",
238           "You can give number of vertical grid cells.", GRID_MIN, GRID_MAX,
239           GRID_DEF, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
240   g_object_class_install_property (gobject_class, PROP_SENSITIVITY,
241       g_param_spec_double ("sensitivity", "Motion Sensitivity",
242           "You can tunning the element motion sensitivity.", SENSITIVITY_MIN,
243           SENSITIVITY_MAX, SENSITIVITY_DEFAULT,
244           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
245   g_object_class_install_property (gobject_class, PROP_THRESHOLD,
246       g_param_spec_double ("threshold", "Lower bound of motion cells number",
247           "Threshold value for motion, when motion cells number greater sum cells * threshold, we show motion.",
248           THRESHOLD_MIN, THRESHOLD_MAX, THRESHOLD_DEFAULT,
249           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
250   g_object_class_install_property (gobject_class, PROP_GAP,
251       g_param_spec_int ("gap",
252           "Gap is time in second, elapsed time from last motion timestamp. ",
253           "If elapsed time minus form last motion timestamp is greater or equal than gap then we post motion finished bus message. ",
254           GAP_MIN, GAP_MAX, GAP_DEF,
255           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
256   g_object_class_install_property (gobject_class, PROP_POSTNOMOTION,
257       g_param_spec_int ("postnomotion", "POSTNOMOTION",
258           "If non 0 post a no_motion event is posted on the bus if no motion is detected for N seconds",
259           POST_NO_MOTION_MIN, POST_NO_MOTION_MAX, POST_NO_MOTION_DEF,
260           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
261   g_object_class_install_property (gobject_class, PROP_MINIMUNMOTIONFRAMES,
262       g_param_spec_int ("minimummotionframes", "MINIMUN MOTION FRAMES",
263           "Define the minimum number of motion frames that trigger a motion event",
264           MINIMUM_MOTION_FRAMES_MIN, MINIMUM_MOTION_FRAMES_MAX,
265           MINIMUM_MOTION_FRAMES_DEF,
266           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
267   g_object_class_install_property (gobject_class, PROP_DISPLAY,
268       g_param_spec_boolean ("display", "Display",
269           "Motion Cells visible or not on Current Frame", FALSE,
270           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
271   g_object_class_install_property (gobject_class, PROP_POSTALLMOTION,
272       g_param_spec_boolean ("postallmotion", "Post All Motion",
273           "Element post bus msg for every motion frame or just motion start and motion stop",
274           FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
275   g_object_class_install_property (gobject_class, PROP_USEALPHA,
276       g_param_spec_boolean ("usealpha", "Use alpha",
277           "Use or not alpha blending on frames with motion cells", TRUE,
278           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
279   g_object_class_install_property (gobject_class, PROP_DATE,
280       g_param_spec_long ("date", "Motion Cell Date",
281           "Current Date in milliseconds", DATE_MIN, DATE_MAX, DATE_DEF,
282           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
283   g_object_class_install_property (gobject_class, PROP_DATAFILE,
284       g_param_spec_string ("datafile", "DataFile",
285           "Location of motioncells data file (empty string means no saving)",
286           NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
287   g_object_class_install_property (gobject_class, PROP_DATAFILE_EXT,
288       g_param_spec_string ("datafileextension", "DataFile Extension",
289           "Extension of datafile", DEF_DATAFILEEXT,
290           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
291   g_object_class_install_property (gobject_class, PROP_MOTIONMASKCOORD,
292       g_param_spec_string ("motionmaskcoords", "Motion Mask with Coordinates",
293           "The upper left x, y and lower right x, y coordinates separated with \":\", "
294           "describe a region. Regions separated with \",\"", NULL,
295           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
296   g_object_class_install_property (gobject_class, PROP_MOTIONMASKCELLSPOS,
297       g_param_spec_string ("motionmaskcellspos",
298           "Motion Mask with Cells Position",
299           "The line and column idx separated with \":\" what cells want we mask-out, "
300           "describe a cell. Cells separated with \",\"", NULL,
301           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
302   g_object_class_install_property (gobject_class, PROP_CELLSCOLOR,
303       g_param_spec_string ("cellscolor", "Color of Motion Cells",
304           "The color of motion cells separated with \",\"", "255,255,0",
305           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
306   g_object_class_install_property (gobject_class, PROP_MOTIONCELLSIDX,
307       g_param_spec_string ("motioncellsidx", "Motion Cells Of Interest(MOCI)",
308           "The line and column idx separated with \":\", "
309           "describe a cell. Cells separated with \",\"", NULL,
310           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
311   g_object_class_install_property (gobject_class, PROP_CALCULATEMOTION,
312       g_param_spec_boolean ("calculatemotion", "Calculate Motion",
313           "If needs calculate motion on frame you need this property setting true otherwise false",
314           TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
315   g_object_class_install_property (gobject_class, PROP_MOTIONCELLTHICKNESS,
316       g_param_spec_int ("motioncellthickness", "Motion Cell Thickness",
317           "Motion Cell Border Thickness, if it's -1 then motion cell will be fill",
318           THICKNESS_MIN, THICKNESS_MAX, THICKNESS_DEF,
319           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
320 }
321
322 /* initialize the new element
323  * instantiate pads and add them to element
324  * set pad callback functions
325  * initialize instance structure
326  */
327 static void
328 gst_motion_cells_init (GstMotioncells * filter, GstMotioncellsClass * gclass)
329 {
330   filter->propset_mutex = g_mutex_new ();
331   filter->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink");
332   gst_pad_set_setcaps_function (filter->sinkpad,
333       GST_DEBUG_FUNCPTR (gst_motion_cells_set_caps));
334   gst_pad_set_getcaps_function (filter->sinkpad,
335       GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps));
336   gst_pad_set_chain_function (filter->sinkpad,
337       GST_DEBUG_FUNCPTR (gst_motion_cells_chain));
338
339   filter->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
340   gst_pad_set_getcaps_function (filter->srcpad,
341       GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps));
342
343   gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad);
344   gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);
345
346   filter->display = TRUE;
347   filter->calculate_motion = TRUE;
348
349   filter->prevgridx = 0;
350   filter->prevgridy = 0;
351   filter->gridx = GRID_DEF;
352   filter->gridy = GRID_DEF;
353   filter->gap = GAP_DEF;
354   filter->postnomotion = POST_NO_MOTION_DEF;
355   filter->minimum_motion_frames = MINIMUM_MOTION_FRAMES_DEF;
356
357   filter->prev_datafile = g_strdup (NULL);
358   filter->cur_datafile = g_strdup (NULL);
359   filter->basename_datafile = g_strdup (NULL);
360   filter->datafile_extension = g_strdup (DEF_DATAFILEEXT);
361   filter->sensitivity = SENSITIVITY_DEFAULT;
362   filter->threshold = THRESHOLD_DEFAULT;
363
364   filter->motionmaskcoord_count = 0;
365   filter->motionmaskcoords = NULL;
366   filter->motionmaskcells_count = 0;
367   filter->motionmaskcellsidx = NULL;
368   filter->motioncellscolor = g_new0 (cellscolor, 1);
369   filter->motioncellscolor->R_channel_value = 255;
370   filter->motioncellscolor->G_channel_value = 255;
371   filter->motioncellscolor->B_channel_value = 0;
372   filter->motioncellsidx = NULL;
373   filter->motioncells_count = 0;
374   filter->motion_begin_timestamp = 0;
375   filter->last_motion_timestamp = 0;
376   filter->last_nomotion_notified = 0;
377   filter->consecutive_motion = 0;
378   filter->motion_timestamp = 0;
379   filter->prev_buff_timestamp = 0;
380   filter->cur_buff_timestamp = 0;
381   filter->diff_timestamp = -1;
382   gettimeofday (&filter->tv, NULL);
383   filter->starttime = 1000 * filter->tv.tv_sec;
384   filter->previous_motion = false;
385   filter->changed_datafile = false;
386   filter->postallmotion = false;
387   filter->usealpha = true;
388   filter->firstdatafile = false;
389   filter->firstgridx = true;
390   filter->firstgridy = true;
391   filter->changed_gridx = false;
392   filter->changed_gridy = false;
393   filter->firstframe = true;
394   filter->changed_startime = false;
395   filter->sent_init_error_msg = false;
396   filter->sent_save_error_msg = false;
397   filter->thickness = THICKNESS_DEF;
398
399   filter->datafileidx = 0;
400   g_mutex_lock (filter->propset_mutex);
401   filter->id = instanceCounter;
402   motion_cells_init ();
403   g_mutex_unlock (filter->propset_mutex);
404
405 }
406
407 static void
408 gst_motion_cells_set_property (GObject * object, guint prop_id,
409     const GValue * value, GParamSpec * pspec)
410 {
411   GstMotioncells *filter = gst_motion_cells (object);
412   //variables for overlay regions setup
413   gchar **strs, **colorstr, **motioncellsstr, **motionmaskcellsstr;
414   int i, ux, uy, lx, ly;
415   int r, g, b;
416   int cellscolorscnt = 0;
417   int linidx, colidx, masklinidx, maskcolidx;
418   int tmpux = -1;
419   int tmpuy = -1;
420   int tmplx = -1;
421   int tmply = -1;
422   GstStateChangeReturn ret;
423
424   g_mutex_lock (filter->propset_mutex);
425   switch (prop_id) {
426     case PROP_GRID_X:
427       ret = gst_element_get_state (GST_ELEMENT (filter),
428           &filter->state, NULL, 250 * GST_NSECOND);
429       filter->gridx = g_value_get_int (value);
430       if (filter->prevgridx != filter->gridx
431           && ret == GST_STATE_CHANGE_SUCCESS
432           && filter->state == GST_STATE_PLAYING) {
433         filter->changed_gridx = true;
434       }
435       filter->prevgridx = filter->gridx;
436       break;
437     case PROP_GRID_Y:
438       ret = gst_element_get_state (GST_ELEMENT (filter),
439           &filter->state, NULL, 250 * GST_NSECOND);
440       filter->gridy = g_value_get_int (value);
441       if (filter->prevgridy != filter->gridy
442           && ret == GST_STATE_CHANGE_SUCCESS
443           && filter->state == GST_STATE_PLAYING) {
444         filter->changed_gridy = true;
445       }
446       filter->prevgridy = filter->gridy;
447       break;
448     case PROP_GAP:
449       filter->gap = g_value_get_int (value);
450       break;
451     case PROP_POSTNOMOTION:
452       filter->postnomotion = g_value_get_int (value);
453       break;
454     case PROP_MINIMUNMOTIONFRAMES:
455       filter->minimum_motion_frames = g_value_get_int (value);
456       break;
457     case PROP_SENSITIVITY:
458       filter->sensitivity = g_value_get_double (value);
459       break;
460     case PROP_THRESHOLD:
461       filter->threshold = g_value_get_double (value);
462       break;
463     case PROP_DISPLAY:
464       filter->display = g_value_get_boolean (value);
465       break;
466     case PROP_POSTALLMOTION:
467       filter->postallmotion = g_value_get_boolean (value);
468       break;
469     case PROP_USEALPHA:
470       filter->usealpha = g_value_get_boolean (value);
471       break;
472     case PROP_CALCULATEMOTION:
473       filter->calculate_motion = g_value_get_boolean (value);
474       break;
475     case PROP_DATE:
476       ret = gst_element_get_state (GST_ELEMENT (filter),
477           &filter->state, NULL, 250 * GST_NSECOND);
478       if (ret == GST_STATE_CHANGE_SUCCESS && filter->state == GST_STATE_PLAYING) {
479         filter->changed_startime = true;
480       }
481       filter->starttime = g_value_get_long (value);
482       break;
483     case PROP_DATAFILE:
484       GFREE (filter->cur_datafile);
485       GFREE (filter->basename_datafile);
486       filter->basename_datafile = g_value_dup_string (value);
487
488       if (strlen (filter->basename_datafile) == 0) {
489         filter->cur_datafile = g_strdup (NULL);
490         break;
491       }
492       filter->cur_datafile =
493           g_strdup_printf ("%s-0.%s", filter->basename_datafile,
494           filter->datafile_extension);
495       if (g_strcmp0 (filter->prev_datafile, filter->basename_datafile) != 0) {
496         filter->changed_datafile = TRUE;
497         filter->sent_init_error_msg = FALSE;
498         filter->sent_save_error_msg = FALSE;
499         filter->datafileidx = 0;
500         motion_cells_free_resources (filter->id);
501       } else {
502         filter->changed_datafile = FALSE;
503       }
504
505       GFREE (filter->prev_datafile);
506       filter->prev_datafile = g_strdup (filter->basename_datafile);
507       break;
508     case PROP_DATAFILE_EXT:
509       GFREE (filter->datafile_extension);
510       filter->datafile_extension = g_value_dup_string (value);
511       break;
512     case PROP_MOTIONMASKCOORD:
513       strs = g_strsplit (g_value_get_string (value), ",", 255);
514       GFREE (filter->motionmaskcoords);
515       //setting number of regions
516       for (filter->motionmaskcoord_count = 0;
517           strs[filter->motionmaskcoord_count] != NULL;
518           ++filter->motionmaskcoord_count);
519       if (filter->motionmaskcoord_count > 0) {
520         sscanf (strs[0], "%d:%d:%d:%d", &tmpux, &tmpuy, &tmplx, &tmply);
521         if (tmpux > -1 && tmpuy > -1 && tmplx > -1 && tmply > -1) {
522           filter->motionmaskcoords =
523               g_new0 (motionmaskcoordrect, filter->motionmaskcoord_count);
524
525           for (i = 0; i < filter->motionmaskcoord_count; ++i) {
526             sscanf (strs[i], "%d:%d:%d:%d", &ux, &uy, &lx, &ly);
527             ux = CLAMP (ux, 0, filter->width - 1);
528             uy = CLAMP (uy, 0, filter->height - 1);
529             lx = CLAMP (lx, 0, filter->width - 1);
530             ly = CLAMP (ly, 0, filter->height - 1);
531             filter->motionmaskcoords[i].upper_left_x = ux;
532             filter->motionmaskcoords[i].upper_left_y = uy;
533             filter->motionmaskcoords[i].lower_right_x = lx;
534             filter->motionmaskcoords[i].lower_right_y = ly;
535           }
536         } else {
537           filter->motionmaskcoord_count = 0;
538         }
539       }
540       if (strs)
541         g_strfreev (strs);
542       tmpux = -1;
543       tmpuy = -1;
544       tmplx = -1;
545       tmply = -1;
546       break;
547     case PROP_MOTIONMASKCELLSPOS:
548       motionmaskcellsstr = g_strsplit (g_value_get_string (value), ",", 255);
549       GFREE (filter->motionmaskcellsidx);
550       //setting number of regions
551       for (filter->motionmaskcells_count = 0;
552           motionmaskcellsstr[filter->motionmaskcells_count] != NULL;
553           ++filter->motionmaskcells_count);
554       if (filter->motionmaskcells_count > 0) {
555         sscanf (motionmaskcellsstr[0], "%d:%d", &tmpux, &tmpuy);
556         if (tmpux > -1 && tmpuy > -1) {
557           filter->motionmaskcellsidx =
558               g_new0 (motioncellidx, filter->motionmaskcells_count);
559           for (i = 0; i < filter->motionmaskcells_count; ++i) {
560             sscanf (motionmaskcellsstr[i], "%d:%d", &masklinidx, &maskcolidx);
561             filter->motionmaskcellsidx[i].lineidx = masklinidx;
562             filter->motionmaskcellsidx[i].columnidx = maskcolidx;
563           }
564         } else {
565           filter->motionmaskcells_count = 0;
566         }
567       }
568       if (motionmaskcellsstr)
569         g_strfreev (motionmaskcellsstr);
570       tmpux = -1;
571       tmpuy = -1;
572       tmplx = -1;
573       tmply = -1;
574       break;
575     case PROP_CELLSCOLOR:
576       colorstr = g_strsplit (g_value_get_string (value), ",", 255);
577       for (cellscolorscnt = 0; colorstr[cellscolorscnt] != NULL;
578           ++cellscolorscnt);
579       if (cellscolorscnt == 3) {
580         sscanf (colorstr[0], "%d", &r);
581         sscanf (colorstr[1], "%d", &g);
582         sscanf (colorstr[2], "%d", &b);
583         //check right RGB color format
584         r = CLAMP (r, 1, 255);
585         g = CLAMP (g, 1, 255);
586         b = CLAMP (b, 1, 255);
587         filter->motioncellscolor->R_channel_value = r;
588         filter->motioncellscolor->G_channel_value = g;
589         filter->motioncellscolor->B_channel_value = b;
590       }
591       if (colorstr)
592         g_strfreev (colorstr);
593       break;
594     case PROP_MOTIONCELLSIDX:
595       motioncellsstr = g_strsplit (g_value_get_string (value), ",", 255);
596
597       //setting number of regions
598       for (filter->motioncells_count = 0;
599           motioncellsstr[filter->motioncells_count] != NULL;
600           ++filter->motioncells_count);
601       if (filter->motioncells_count > 0) {
602         sscanf (motioncellsstr[0], "%d:%d", &tmpux, &tmpuy);
603         if (tmpux > -1 && tmpuy > -1) {
604           GFREE (filter->motioncellsidx);
605
606           filter->motioncellsidx =
607               g_new0 (motioncellidx, filter->motioncells_count);
608
609           for (i = 0; i < filter->motioncells_count; ++i) {
610             sscanf (motioncellsstr[i], "%d:%d", &linidx, &colidx);
611             filter->motioncellsidx[i].lineidx = linidx;
612             filter->motioncellsidx[i].columnidx = colidx;
613           }
614         } else {
615           filter->motioncells_count = 0;
616         }
617       }
618       if (motioncellsstr)
619         g_strfreev (motioncellsstr);
620       tmpux = -1;
621       tmpuy = -1;
622       tmplx = -1;
623       tmply = -1;
624       break;
625     case PROP_MOTIONCELLTHICKNESS:
626       filter->thickness = g_value_get_int (value);
627       break;
628     default:
629       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
630       break;
631   }
632   g_mutex_unlock (filter->propset_mutex);
633 }
634
635 static void
636 gst_motion_cells_get_property (GObject * object, guint prop_id,
637     GValue * value, GParamSpec * pspec)
638 {
639   GstMotioncells *filter = gst_motion_cells (object);
640   GString *str;
641   int i;
642
643   switch (prop_id) {
644     case PROP_GRID_X:
645       g_value_set_int (value, filter->gridx);
646       break;
647     case PROP_GRID_Y:
648       g_value_set_int (value, filter->gridy);
649       break;
650     case PROP_GAP:
651       g_value_set_int (value, filter->gap);
652       break;
653     case PROP_POSTNOMOTION:
654       g_value_set_int (value, filter->postnomotion);
655       break;
656     case PROP_MINIMUNMOTIONFRAMES:
657       g_value_set_int (value, filter->minimum_motion_frames);
658       break;
659     case PROP_SENSITIVITY:
660       g_value_set_double (value, filter->sensitivity);
661       break;
662     case PROP_THRESHOLD:
663       g_value_set_double (value, filter->threshold);
664       break;
665     case PROP_DISPLAY:
666       g_value_set_boolean (value, filter->display);
667       break;
668     case PROP_POSTALLMOTION:
669       g_value_set_boolean (value, filter->postallmotion);
670       break;
671     case PROP_USEALPHA:
672       g_value_set_boolean (value, filter->usealpha);
673       break;
674     case PROP_CALCULATEMOTION:
675       g_value_set_boolean (value, filter->calculate_motion);
676       break;
677     case PROP_DATE:
678       g_value_set_long (value, filter->starttime);
679       break;
680     case PROP_DATAFILE:
681       g_value_set_string (value, filter->basename_datafile);
682       break;
683     case PROP_DATAFILE_EXT:
684       g_value_set_string (value, filter->datafile_extension);
685       break;
686     case PROP_MOTIONMASKCOORD:
687       str = g_string_new ("");
688       for (i = 0; i < filter->motionmaskcoord_count; ++i) {
689         if (i < filter->motionmaskcoord_count - 1)
690           g_string_append_printf (str, "%d:%d:%d:%d,",
691               filter->motionmaskcoords[i].upper_left_x,
692               filter->motionmaskcoords[i].upper_left_y,
693               filter->motionmaskcoords[i].lower_right_x,
694               filter->motionmaskcoords[i].lower_right_y);
695         else
696           g_string_append_printf (str, "%d:%d:%d:%d",
697               filter->motionmaskcoords[i].upper_left_x,
698               filter->motionmaskcoords[i].upper_left_y,
699               filter->motionmaskcoords[i].lower_right_x,
700               filter->motionmaskcoords[i].lower_right_y);
701
702       }
703       g_value_set_string (value, str->str);
704       g_string_free (str, TRUE);
705       break;
706     case PROP_MOTIONMASKCELLSPOS:
707       str = g_string_new ("");
708       for (i = 0; i < filter->motionmaskcells_count; ++i) {
709         if (i < filter->motionmaskcells_count - 1)
710           g_string_append_printf (str, "%d:%d,",
711               filter->motionmaskcellsidx[i].lineidx,
712               filter->motionmaskcellsidx[i].columnidx);
713         else
714           g_string_append_printf (str, "%d:%d",
715               filter->motionmaskcellsidx[i].lineidx,
716               filter->motionmaskcellsidx[i].columnidx);
717       }
718       g_value_set_string (value, str->str);
719       g_string_free (str, TRUE);
720       break;
721     case PROP_CELLSCOLOR:
722       str = g_string_new ("");
723
724       g_string_printf (str, "%d,%d,%d",
725           filter->motioncellscolor->R_channel_value,
726           filter->motioncellscolor->G_channel_value,
727           filter->motioncellscolor->B_channel_value);
728
729       g_value_set_string (value, str->str);
730       g_string_free (str, TRUE);
731       break;
732     case PROP_MOTIONCELLSIDX:
733       str = g_string_new ("");
734       for (i = 0; i < filter->motioncells_count; ++i) {
735         if (i < filter->motioncells_count - 1)
736           g_string_append_printf (str, "%d:%d,",
737               filter->motioncellsidx[i].lineidx,
738               filter->motioncellsidx[i].columnidx);
739         else
740           g_string_append_printf (str, "%d:%d",
741               filter->motioncellsidx[i].lineidx,
742               filter->motioncellsidx[i].columnidx);
743       }
744       g_value_set_string (value, str->str);
745       g_string_free (str, TRUE);
746       break;
747     case PROP_MOTIONCELLTHICKNESS:
748       g_value_set_int (value, filter->thickness);
749       break;
750     default:
751       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
752       break;
753   }
754 }
755
756 static void
757 gst_motioncells_update_motion_cells (GstMotioncells * filter)
758 {
759   int i = 0;
760   int cellscnt = 0;
761   int j = 0;
762   int newcellscnt;
763   motioncellidx *motioncellsidx;
764   for (i = 0; i < filter->motioncells_count; i++) {
765     if ((filter->gridx <= filter->motioncellsidx[i].columnidx) ||
766         (filter->gridy <= filter->motioncellsidx[i].lineidx)) {
767       cellscnt++;
768     }
769   }
770   newcellscnt = filter->motioncells_count - cellscnt;
771   motioncellsidx = g_new0 (motioncellidx, newcellscnt);
772   for (i = 0; i < filter->motioncells_count; i++) {
773     if ((filter->motioncellsidx[i].lineidx < filter->gridy) &&
774         (filter->motioncellsidx[i].columnidx < filter->gridx)) {
775       motioncellsidx[j].lineidx = filter->motioncellsidx[i].lineidx;
776       motioncellsidx[j].columnidx = filter->motioncellsidx[i].columnidx;
777       j++;
778     }
779   }
780   GFREE (filter->motioncellsidx);
781   filter->motioncells_count = newcellscnt;
782   filter->motioncellsidx = g_new0 (motioncellidx, filter->motioncells_count);
783   j = 0;
784   for (i = 0; i < filter->motioncells_count; i++) {
785     filter->motioncellsidx[i].lineidx = motioncellsidx[j].lineidx;
786     filter->motioncellsidx[i].columnidx = motioncellsidx[j].columnidx;
787     j++;
788   }
789   GFREE (motioncellsidx);
790 }
791
792 static void
793 gst_motioncells_update_motion_masks (GstMotioncells * filter)
794 {
795
796   int i = 0;
797   int maskcnt = 0;
798   int j = 0;
799   int newmaskcnt;
800   motioncellidx *motionmaskcellsidx;
801   for (i = 0; i < filter->motionmaskcells_count; i++) {
802     if ((filter->gridx <= filter->motionmaskcellsidx[i].columnidx) ||
803         (filter->gridy <= filter->motionmaskcellsidx[i].lineidx)) {
804       maskcnt++;
805     }
806   }
807   newmaskcnt = filter->motionmaskcells_count - maskcnt;
808   motionmaskcellsidx = g_new0 (motioncellidx, newmaskcnt);
809   for (i = 0; i < filter->motionmaskcells_count; i++) {
810     if ((filter->motionmaskcellsidx[i].lineidx < filter->gridy) &&
811         (filter->motionmaskcellsidx[i].columnidx < filter->gridx)) {
812       motionmaskcellsidx[j].lineidx = filter->motionmaskcellsidx[i].lineidx;
813       motionmaskcellsidx[j].columnidx = filter->motionmaskcellsidx[i].columnidx;
814       j++;
815     }
816   }
817   GFREE (filter->motionmaskcellsidx);
818   filter->motionmaskcells_count = newmaskcnt;
819   filter->motionmaskcellsidx =
820       g_new0 (motioncellidx, filter->motionmaskcells_count);
821   j = 0;
822   for (i = 0; i < filter->motionmaskcells_count; i++) {
823     filter->motionmaskcellsidx[i].lineidx = motionmaskcellsidx[j].lineidx;
824     filter->motionmaskcellsidx[i].columnidx = motionmaskcellsidx[j].columnidx;
825     j++;
826   }
827   GFREE (motionmaskcellsidx);
828 }
829
830 /* GstElement vmethod implementations */
831
832 /* this function handles the link with other elements */
833 static gboolean
834 gst_motion_cells_set_caps (GstPad * pad, GstCaps * caps)
835 {
836   GstMotioncells *filter;
837   GstPad *otherpad;
838   GstStructure *structure;
839   int numerator, denominator;
840
841   filter = gst_motion_cells (gst_pad_get_parent (pad));
842   structure = gst_caps_get_structure (caps, 0);
843   gst_structure_get_int (structure, "width", &filter->width);
844   gst_structure_get_int (structure, "height", &filter->height);
845   gst_structure_get_fraction (structure, "framerate", &numerator, &denominator);
846   filter->framerate = (double) numerator / (double) denominator;
847   if (filter->cvImage)
848     cvReleaseImage (&filter->cvImage);
849   filter->cvImage =
850       cvCreateImage (cvSize (filter->width, filter->height), IPL_DEPTH_8U, 3);
851
852   otherpad = (pad == filter->srcpad) ? filter->sinkpad : filter->srcpad;
853   gst_object_unref (filter);
854
855   return gst_pad_set_caps (otherpad, caps);
856 }
857
858 /* chain function
859  * this function does the actual processing
860  */
861 static GstFlowReturn
862 gst_motion_cells_chain (GstPad * pad, GstBuffer * buf)
863 {
864
865   GstMotioncells *filter;
866
867   filter = gst_motion_cells (GST_OBJECT_PARENT (pad));
868   if (filter->calculate_motion) {
869     double sensitivity;
870     int framerate, gridx, gridy, motionmaskcells_count, motionmaskcoord_count,
871         motioncells_count, i;
872     int thickness, success, motioncellsidxcnt, numberOfCells,
873         motioncellsnumber, cellsOfInterestNumber;
874     int mincellsOfInterestNumber, motiondetect;
875     char *datafile;
876     bool display, changed_datafile, useAlpha;
877     gint64 starttime;
878     motionmaskcoordrect *motionmaskcoords;
879     motioncellidx *motionmaskcellsidx;
880     cellscolor motioncellscolor;
881     motioncellidx *motioncellsidx;
882     g_mutex_lock (filter->propset_mutex);
883     buf = gst_buffer_make_writable (buf);
884     filter->cvImage->imageData = (char *) GST_BUFFER_DATA (buf);
885     if (filter->firstframe) {
886       setPrevFrame (filter->cvImage, filter->id);
887       filter->firstframe = FALSE;
888     }
889
890     sensitivity = filter->sensitivity;
891     framerate = filter->framerate;
892     gridx = filter->gridx;
893     gridy = filter->gridy;
894     display = filter->display;
895     motionmaskcoord_count = filter->motionmaskcoord_count;
896     motionmaskcoords =
897         g_new0 (motionmaskcoordrect, filter->motionmaskcoord_count);
898     for (i = 0; i < filter->motionmaskcoord_count; i++) {       //we need divide 2 because we use gauss pyramid in C++ side
899       motionmaskcoords[i].upper_left_x =
900           filter->motionmaskcoords[i].upper_left_x / 2;
901       motionmaskcoords[i].upper_left_y =
902           filter->motionmaskcoords[i].upper_left_y / 2;
903       motionmaskcoords[i].lower_right_x =
904           filter->motionmaskcoords[i].lower_right_x / 2;
905       motionmaskcoords[i].lower_right_y =
906           filter->motionmaskcoords[i].lower_right_y / 2;
907     }
908
909     motioncellscolor.R_channel_value =
910         filter->motioncellscolor->R_channel_value;
911     motioncellscolor.G_channel_value =
912         filter->motioncellscolor->G_channel_value;
913     motioncellscolor.B_channel_value =
914         filter->motioncellscolor->B_channel_value;
915
916     if ((filter->changed_gridx || filter->changed_gridy
917             || filter->changed_startime)) {
918       if ((g_strcmp0 (filter->cur_datafile, NULL) != 0)) {
919         GFREE (filter->cur_datafile);
920         filter->datafileidx++;
921         filter->cur_datafile =
922             g_strdup_printf ("%s-%d.%s", filter->basename_datafile,
923             filter->datafileidx, filter->datafile_extension);
924         filter->changed_datafile = TRUE;
925         motion_cells_free_resources (filter->id);
926       }
927       if (filter->motioncells_count > 0)
928         gst_motioncells_update_motion_cells (filter);
929       if (filter->motionmaskcells_count > 0)
930         gst_motioncells_update_motion_masks (filter);
931       filter->changed_gridx = FALSE;
932       filter->changed_gridy = FALSE;
933       filter->changed_startime = FALSE;
934     }
935     datafile = g_strdup (filter->cur_datafile);
936     filter->cur_buff_timestamp = (GST_BUFFER_TIMESTAMP (buf) / GST_MSECOND);
937     filter->starttime +=
938         (filter->cur_buff_timestamp - filter->prev_buff_timestamp);
939     starttime = filter->starttime;
940     if (filter->changed_datafile || filter->diff_timestamp < 0)
941       filter->diff_timestamp =
942           (gint64) (GST_BUFFER_TIMESTAMP (buf) / GST_MSECOND);
943     changed_datafile = filter->changed_datafile;
944     motionmaskcells_count = filter->motionmaskcells_count;
945     motionmaskcellsidx = g_new0 (motioncellidx, filter->motionmaskcells_count);
946     for (i = 0; i < filter->motionmaskcells_count; i++) {
947       motionmaskcellsidx[i].lineidx = filter->motionmaskcellsidx[i].lineidx;
948       motionmaskcellsidx[i].columnidx = filter->motionmaskcellsidx[i].columnidx;
949     }
950     motioncells_count = filter->motioncells_count;
951     motioncellsidx = g_new0 (motioncellidx, filter->motioncells_count);
952     for (i = 0; i < filter->motioncells_count; i++) {
953       motioncellsidx[i].lineidx = filter->motioncellsidx[i].lineidx;
954       motioncellsidx[i].columnidx = filter->motioncellsidx[i].columnidx;
955     }
956     useAlpha = filter->usealpha;
957     thickness = filter->thickness;
958     success =
959         perform_detection_motion_cells (filter->cvImage, sensitivity, framerate,
960         gridx, gridy,
961         (gint64) (GST_BUFFER_TIMESTAMP (buf) / GST_MSECOND) -
962         filter->diff_timestamp, display, useAlpha, motionmaskcoord_count,
963         motionmaskcoords, motionmaskcells_count, motionmaskcellsidx,
964         motioncellscolor, motioncells_count, motioncellsidx, starttime,
965         datafile, changed_datafile, thickness, filter->id);
966     if ((success == 1) && (filter->sent_init_error_msg == false)) {
967       char *initfailedreason;
968       int initerrorcode;
969       GstStructure *s;
970       GstMessage *m;
971       initfailedreason = getInitDataFileFailed (filter->id);
972       initerrorcode = getInitErrorCode (filter->id);
973       s = gst_structure_new ("motion", "init_error_code", G_TYPE_INT,
974           initerrorcode, "details", G_TYPE_STRING, initfailedreason, NULL);
975       m = gst_message_new_element (GST_OBJECT (filter), s);
976       gst_element_post_message (GST_ELEMENT (filter), m);
977       filter->sent_init_error_msg = TRUE;
978     }
979     if ((success == -1) && (filter->sent_save_error_msg == false)) {
980       char *savefailedreason;
981       int saveerrorcode;
982       GstStructure *s;
983       GstMessage *m;
984       savefailedreason = getSaveDataFileFailed (filter->id);
985       saveerrorcode = getSaveErrorCode (filter->id);
986       s = gst_structure_new ("motion", "save_error_code", G_TYPE_INT,
987           saveerrorcode, "details", G_TYPE_STRING, savefailedreason, NULL);
988       m = gst_message_new_element (GST_OBJECT (filter), s);
989       gst_element_post_message (GST_ELEMENT (filter), m);
990       filter->sent_save_error_msg = TRUE;
991     }
992     if (success == -2) {        //frame dropped
993       filter->prev_buff_timestamp = filter->cur_buff_timestamp;
994       //free
995       GFREE (datafile);
996       GFREE (motionmaskcoords);
997       GFREE (motionmaskcellsidx);
998       GFREE (motioncellsidx);
999       g_mutex_unlock (filter->propset_mutex);
1000       return gst_pad_push (filter->srcpad, buf);
1001     }
1002     filter->changed_datafile = getChangedDataFile (filter->id);
1003     motioncellsidxcnt = getMotionCellsIdxCnt (filter->id);
1004     numberOfCells = filter->gridx * filter->gridy;
1005     motioncellsnumber = motioncellsidxcnt / MSGLEN;
1006     cellsOfInterestNumber = (filter->motioncells_count > 0) ?   //how many cells interest for us
1007         (filter->motioncells_count) : (numberOfCells);
1008     mincellsOfInterestNumber =
1009         floor ((double) cellsOfInterestNumber * filter->threshold);
1010     motiondetect = (motioncellsnumber >= mincellsOfInterestNumber) ? 1 : 0;
1011     if ((motioncellsidxcnt > 0) && (motiondetect == 1)) {
1012       char *detectedmotioncells;
1013       filter->last_motion_timestamp = GST_BUFFER_TIMESTAMP (buf);
1014       detectedmotioncells = getMotionCellsIdx (filter->id);
1015       if (detectedmotioncells) {
1016         filter->consecutive_motion++;
1017         if ((filter->previous_motion == false)
1018             && (filter->consecutive_motion >= filter->minimum_motion_frames)) {
1019           GstStructure *s;
1020           GstMessage *m;
1021           filter->previous_motion = true;
1022           filter->motion_begin_timestamp = GST_BUFFER_TIMESTAMP (buf);
1023           s = gst_structure_new ("motion", "motion_cells_indices",
1024               G_TYPE_STRING, detectedmotioncells, "motion_begin", G_TYPE_UINT64,
1025               filter->motion_begin_timestamp, NULL);
1026           m = gst_message_new_element (GST_OBJECT (filter), s);
1027           gst_element_post_message (GST_ELEMENT (filter), m);
1028         } else if (filter->postallmotion) {
1029           GstStructure *s;
1030           GstMessage *m;
1031           filter->motion_timestamp = GST_BUFFER_TIMESTAMP (buf);
1032           s = gst_structure_new ("motion", "motion_cells_indices",
1033               G_TYPE_STRING, detectedmotioncells, "motion", G_TYPE_UINT64,
1034               filter->motion_timestamp, NULL);
1035           m = gst_message_new_element (GST_OBJECT (filter), s);
1036           gst_element_post_message (GST_ELEMENT (filter), m);
1037         }
1038       } else {
1039         GstStructure *s;
1040         GstMessage *m;
1041         s = gst_structure_new ("motion", "motion_cells_indices", G_TYPE_STRING,
1042             "error", NULL);
1043         m = gst_message_new_element (GST_OBJECT (filter), s);
1044         gst_element_post_message (GST_ELEMENT (filter), m);
1045       }
1046     } else {
1047       filter->consecutive_motion = 0;
1048       if ((((GST_BUFFER_TIMESTAMP (buf) -
1049                       filter->last_motion_timestamp) / 1000000000l) >=
1050               filter->gap)
1051           && (filter->last_motion_timestamp > 0)) {
1052         GST_DEBUG ("POST MOTION FINISHED MSG\n");
1053         if (filter->previous_motion) {
1054           GstStructure *s;
1055           GstMessage *m;
1056           filter->previous_motion = false;
1057           s = gst_structure_new ("motion", "motion_finished", G_TYPE_UINT64,
1058               filter->last_motion_timestamp, NULL);
1059           m = gst_message_new_element (GST_OBJECT (filter), s);
1060           gst_element_post_message (GST_ELEMENT (filter), m);
1061         }
1062       }
1063     }
1064     if (filter->postnomotion > 0) {
1065       guint64 last_buf_timestamp = GST_BUFFER_TIMESTAMP (buf) / 1000000000l;
1066       if ((last_buf_timestamp -
1067               (filter->last_motion_timestamp / 1000000000l)) >=
1068           filter->postnomotion) {
1069         GST_DEBUG ("POST NO MOTION MSG\n");
1070         if ((last_buf_timestamp -
1071                 (filter->last_nomotion_notified / 1000000000l)) >=
1072             filter->postnomotion) {
1073           GstStructure *s;
1074           GstMessage *m;
1075           filter->last_nomotion_notified = GST_BUFFER_TIMESTAMP (buf);
1076           s = gst_structure_new ("motion", "no_motion", G_TYPE_UINT64,
1077               filter->last_motion_timestamp, NULL);
1078           m = gst_message_new_element (GST_OBJECT (filter), s);
1079           gst_element_post_message (GST_ELEMENT (filter), m);
1080         }
1081       }
1082     }
1083     filter->prev_buff_timestamp = filter->cur_buff_timestamp;
1084     //free
1085     GFREE (datafile);
1086     GFREE (motionmaskcoords);
1087     GFREE (motionmaskcellsidx);
1088     GFREE (motioncellsidx);
1089
1090     g_mutex_unlock (filter->propset_mutex);
1091   }
1092
1093   return gst_pad_push (filter->srcpad, buf);
1094 }
1095
1096 /* entry point to initialize the plug-in
1097  * initialize the plug-in itself
1098  * register the element factories and other features
1099  */
1100 gboolean
1101 gst_motioncells_plugin_init (GstPlugin * plugin)
1102 {
1103   /* debug category for fltering log messages */
1104   GST_DEBUG_CATEGORY_INIT (gst_motion_cells_debug,
1105       "motioncells",
1106       0,
1107       "Performs motion detection on videos, providing detected positions via bus messages");
1108
1109   return gst_element_register (plugin, "motioncells", GST_RANK_NONE,
1110       GST_TYPE_MOTIONCELLS);
1111 }