• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * GStreamer MotionCells detect areas of motion
3  * Copyright (C) 2011 Robert Jobbagy <jobbagy.robert@gmail.com>
4  * Copyright (C) 2011 -2018 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., 51 Franklin St, Fifth Floor,
42  * Boston, MA 02110-1301, USA.
43  */
44 
45 /**
46  * SECTION:element-motioncells
47  *
48  * Performs motion detection on videos.
49  *
50  * ## Example launch line
51  *
52  * |[
53  * gst-launch-1.0 videotestsrc pattern=18 ! videorate ! videoscale ! video/x-raw,width=320,height=240,framerate=5/1 ! videoconvert ! motioncells ! videoconvert ! xvimagesink
54  * ]|
55  */
56 
57 #ifdef HAVE_CONFIG_H
58 #  include <config.h>
59 #endif
60 
61 #include "gstmotioncells.h"
62 
63 GST_DEBUG_CATEGORY_STATIC (gst_motion_cells_debug);
64 #define GST_CAT_DEFAULT gst_motion_cells_debug
65 
66 #define GRID_DEF 10
67 #define GRID_MIN 8
68 #define GRID_MAX 32
69 #define SENSITIVITY_DEFAULT 0.5
70 #define SENSITIVITY_MIN 0
71 #define SENSITIVITY_MAX 1
72 #define THRESHOLD_MIN 0
73 #define THRESHOLD_DEFAULT 0.01
74 #define THRESHOLD_MAX 1.0
75 #define GAP_MIN 1
76 #define GAP_DEF 5
77 #define GAP_MAX 60
78 #define POST_NO_MOTION_MIN 0
79 #define POST_NO_MOTION_DEF 0
80 #define POST_NO_MOTION_MAX 180
81 #define MINIMUM_MOTION_FRAMES_MIN 1
82 #define MINIMUM_MOTION_FRAMES_DEF 1
83 #define MINIMUM_MOTION_FRAMES_MAX 60
84 #define THICKNESS_MIN -1
85 #define THICKNESS_DEF 1
86 #define THICKNESS_MAX 5
87 #define DATE_MIN 0
88 #define DATE_DEF 1
89 #define DATE_MAX LONG_MAX
90 #define DEF_DATAFILEEXT "vamc"
91 #define MSGLEN 6
92 #define BUSMSGLEN 20
93 
94 #define GFREE(POINTER)\
95 		{\
96 			g_free(POINTER);\
97 			POINTER = NULL;\
98 		}
99 
100 /* Filter signals and args */
101 enum
102 {
103   /* FILL ME */
104   LAST_SIGNAL
105 };
106 
107 enum
108 {
109   PROP_0,
110   PROP_GRID_X,
111   PROP_GRID_Y,
112   PROP_SENSITIVITY,
113   PROP_THRESHOLD,
114   PROP_DISPLAY,
115   PROP_DATE,
116   PROP_DATAFILE,
117   PROP_DATAFILE_EXT,
118   PROP_MOTIONMASKCOORD,
119   PROP_MOTIONMASKCELLSPOS,
120   PROP_CELLSCOLOR,
121   PROP_MOTIONCELLSIDX,
122   PROP_GAP,
123   PROP_POSTNOMOTION,
124   PROP_MINIMUNMOTIONFRAMES,
125   PROP_CALCULATEMOTION,
126   PROP_POSTALLMOTION,
127   PROP_USEALPHA,
128   PROP_MOTIONCELLTHICKNESS
129 };
130 
131 /* the capabilities of the inputs and outputs.
132  */
133 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
134     GST_PAD_SINK,
135     GST_PAD_ALWAYS,
136     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("RGB")));
137 
138 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
139     GST_PAD_SRC,
140     GST_PAD_ALWAYS,
141     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("RGB")));
142 
143 G_DEFINE_TYPE_WITH_CODE (GstMotioncells, gst_motion_cells,
144     GST_TYPE_OPENCV_VIDEO_FILTER,
145     GST_DEBUG_CATEGORY_INIT (gst_motion_cells_debug, "motioncells", 0,
146         "Performs motion detection on videos, providing detected positions via bus messages"););
147 GST_ELEMENT_REGISTER_DEFINE (motioncells, "motioncells", GST_RANK_NONE,
148     GST_TYPE_MOTIONCELLS);
149 
150 static void gst_motion_cells_set_property (GObject * object, guint prop_id,
151     const GValue * value, GParamSpec * pspec);
152 static void gst_motion_cells_get_property (GObject * object, guint prop_id,
153     GValue * value, GParamSpec * pspec);
154 
155 static gboolean gst_motion_cells_handle_sink_event (GstPad * pad,
156     GstObject * parent, GstEvent * event);
157 static GstFlowReturn gst_motion_cells_transform_ip (GstOpencvVideoFilter *
158     filter, GstBuffer * buf, cv::Mat img);
159 
160 static void gst_motioncells_update_motion_cells (GstMotioncells * filter);
161 static void gst_motioncells_update_motion_masks (GstMotioncells * filter);
162 
163 /* Clean up */
164 static void
gst_motion_cells_finalize(GObject * obj)165 gst_motion_cells_finalize (GObject * obj)
166 {
167   GstMotioncells *filter = gst_motion_cells (obj);
168 
169   motion_cells_free (filter->id);
170 
171   //freeing previously allocated dynamic array
172   if (filter->motionmaskcoord_count > 0) {
173     GFREE (filter->motionmaskcoords);
174   }
175 
176   if (filter->motionmaskcells_count > 0) {
177     GFREE (filter->motionmaskcellsidx);
178   }
179   if (filter->motioncells_count > 0) {
180     GFREE (filter->motioncellsidx);
181   }
182 
183   GFREE (filter->motioncellscolor);
184   GFREE (filter->prev_datafile);
185   GFREE (filter->cur_datafile);
186   GFREE (filter->basename_datafile);
187   GFREE (filter->datafile_extension);
188 
189   G_OBJECT_CLASS (gst_motion_cells_parent_class)->finalize (obj);
190 }
191 
192 /* initialize the motioncells's class */
193 static void
gst_motion_cells_class_init(GstMotioncellsClass * klass)194 gst_motion_cells_class_init (GstMotioncellsClass * klass)
195 {
196   GObjectClass *gobject_class;
197   GstOpencvVideoFilterClass *gstopencvbasefilter_class;
198 
199   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
200   gobject_class = (GObjectClass *) klass;
201   gstopencvbasefilter_class = (GstOpencvVideoFilterClass *) klass;
202 
203   gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_motion_cells_finalize);
204   gobject_class->set_property = gst_motion_cells_set_property;
205   gobject_class->get_property = gst_motion_cells_get_property;
206 
207   gstopencvbasefilter_class->cv_trans_ip_func = gst_motion_cells_transform_ip;
208 
209   g_object_class_install_property (gobject_class, PROP_GRID_X,
210       g_param_spec_int ("gridx", "Number of Horizontal Grids",
211           "Number of horizontal grid cells.", GRID_MIN, GRID_MAX,
212           GRID_DEF,
213           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
214   g_object_class_install_property (gobject_class, PROP_GRID_Y,
215       g_param_spec_int ("gridy", "Number of Vertical Grids",
216           "Number of vertical grid cells.", GRID_MIN, GRID_MAX,
217           GRID_DEF,
218           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
219   g_object_class_install_property (gobject_class, PROP_SENSITIVITY,
220       g_param_spec_double ("sensitivity", "Motion Sensitivity",
221           "Motion detection sensitivity.", SENSITIVITY_MIN,
222           SENSITIVITY_MAX, SENSITIVITY_DEFAULT,
223           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
224   g_object_class_install_property (gobject_class, PROP_THRESHOLD,
225       g_param_spec_double ("threshold", "Lower bound of motion cells number",
226           "Threshold value for motion. Filter detects motion when at least "
227           "this fraction of the cells have moved",
228           THRESHOLD_MIN, THRESHOLD_MAX, THRESHOLD_DEFAULT,
229           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
230   g_object_class_install_property (gobject_class, PROP_GAP,
231       g_param_spec_int ("gap", "Motion-finished Threshold",
232           "Interval in seconds after which motion is considered finished "
233           "and a motion finished bus message is posted.",
234           GAP_MIN, GAP_MAX, GAP_DEF,
235           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
236   g_object_class_install_property (gobject_class, PROP_POSTNOMOTION,
237       g_param_spec_int ("postnomotion", "No-motion Threshold",
238           "If non 0, post a no_motion event on the bus if no motion is "
239           "detected for the given number of seconds",
240           POST_NO_MOTION_MIN, POST_NO_MOTION_MAX, POST_NO_MOTION_DEF,
241           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
242   g_object_class_install_property (gobject_class, PROP_MINIMUNMOTIONFRAMES,
243       g_param_spec_int ("minimummotionframes", "Minimum Motion Frames",
244           "Minimum number of motion frames triggering a motion event",
245           MINIMUM_MOTION_FRAMES_MIN, MINIMUM_MOTION_FRAMES_MAX,
246           MINIMUM_MOTION_FRAMES_DEF,
247           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
248   g_object_class_install_property (gobject_class, PROP_DISPLAY,
249       g_param_spec_boolean ("display", "Display",
250           "Toggle display of motion cells on current frame", FALSE,
251           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
252   g_object_class_install_property (gobject_class, PROP_POSTALLMOTION,
253       g_param_spec_boolean ("postallmotion", "Post All Motion",
254           "Post bus messages for every motion frame or just motion start and "
255           "motion stop",
256           FALSE, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
257   g_object_class_install_property (gobject_class, PROP_USEALPHA,
258       g_param_spec_boolean ("usealpha", "Use alpha",
259           "Toggle usage of alpha blending on frames with motion cells", TRUE,
260           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
261 #if 0
262   /* FIXME: should not be a long property, make it either gint or gint64
263    * (is this property actually used or useful for anything?) */
264   g_object_class_install_property (gobject_class, PROP_DATE,
265       g_param_spec_long ("date", "Motion Cell Date",
266           "Current Date in milliseconds", DATE_MIN, DATE_MAX, DATE_DEF,
267           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
268 #endif
269   g_object_class_install_property (gobject_class, PROP_DATAFILE,
270       g_param_spec_string ("datafile", "DataFile",
271           "Location of motioncells data file (empty string means no saving)",
272           NULL, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
273   g_object_class_install_property (gobject_class, PROP_DATAFILE_EXT,
274       g_param_spec_string ("datafileextension", "DataFile Extension",
275           "Extension of datafile", DEF_DATAFILEEXT,
276           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
277   g_object_class_install_property (gobject_class, PROP_MOTIONMASKCOORD,
278       g_param_spec_string ("motionmaskcoords", "Motion Mask with Coordinates",
279           "Describe a region with its upper left and lower right x, y "
280           "coordinates separated with \":\". Pass multiple regions as a "
281           "comma-separated list", NULL,
282           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
283   g_object_class_install_property (gobject_class, PROP_MOTIONMASKCELLSPOS,
284       g_param_spec_string ("motionmaskcellspos",
285           "Motion Mask with Cells Position",
286           "Describe a cell with its line and column idx separated with \":\". "
287           "Pass multiple cells as a comma-separated list", NULL,
288           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
289   g_object_class_install_property (gobject_class, PROP_CELLSCOLOR,
290       g_param_spec_string ("cellscolor", "Color of Motion Cells",
291           "Color for motion cells in R,G,B format. Max per channel is 255",
292           "255,255,0",
293           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
294   g_object_class_install_property (gobject_class, PROP_MOTIONCELLSIDX,
295       g_param_spec_string ("motioncellsidx", "Motion Cells Of Interest(MOCI)",
296           "Describe a cell with its line and column idx separated with \":\". "
297           "Pass multiple cells as a comma-separated list", NULL,
298           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
299   g_object_class_install_property (gobject_class, PROP_CALCULATEMOTION,
300       g_param_spec_boolean ("calculatemotion", "Calculate Motion",
301           "Toggles motion calculation. If FALSE, this filter does nothing",
302           TRUE, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
303   g_object_class_install_property (gobject_class, PROP_MOTIONCELLTHICKNESS,
304       g_param_spec_int ("motioncellthickness", "Motion Cell Thickness",
305           "Motion Cell Border Thickness. Set to -1 to fill motion cell",
306           THICKNESS_MIN, THICKNESS_MAX, THICKNESS_DEF,
307           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
308 
309   gst_element_class_set_static_metadata (element_class,
310       "motioncells",
311       "Filter/Effect/Video",
312       "Performs motion detection on videos and images, providing detected motion cells index via bus messages",
313       "Robert Jobbagy <jobbagy dot robert at gmail dot com>, Nicola Murino <nicola dot murino at gmail.com>");
314 
315   gst_element_class_add_static_pad_template (element_class, &src_factory);
316   gst_element_class_add_static_pad_template (element_class, &sink_factory);
317 }
318 
319 /* initialize the new element
320  * instantiate pads and add them to element
321  * set pad callback functions
322  * initialize instance structure
323  */
324 static void
gst_motion_cells_init(GstMotioncells * filter)325 gst_motion_cells_init (GstMotioncells * filter)
326 {
327   gst_pad_set_event_function (GST_BASE_TRANSFORM_SINK_PAD (filter),
328       GST_DEBUG_FUNCPTR (gst_motion_cells_handle_sink_event));
329 
330   filter->display = TRUE;
331   filter->calculate_motion = TRUE;
332 
333   filter->prevgridx = 0;
334   filter->prevgridy = 0;
335   filter->gridx = GRID_DEF;
336   filter->gridy = GRID_DEF;
337   filter->gap = GAP_DEF;
338   filter->postnomotion = POST_NO_MOTION_DEF;
339   filter->minimum_motion_frames = MINIMUM_MOTION_FRAMES_DEF;
340 
341   filter->prev_datafile = NULL;
342   filter->cur_datafile = NULL;
343   filter->basename_datafile = NULL;
344   filter->has_delayed_mask = 0;
345   filter->datafile_extension = g_strdup (DEF_DATAFILEEXT);
346   filter->sensitivity = SENSITIVITY_DEFAULT;
347   filter->threshold = THRESHOLD_DEFAULT;
348 
349   filter->motionmaskcoord_count = 0;
350   filter->motionmaskcoords = NULL;
351   filter->motionmaskcells_count = 0;
352   filter->motionmaskcellsidx = NULL;
353   filter->motioncellscolor = g_new0 (cellscolor, 1);
354   filter->motioncellscolor->R_channel_value = 255;
355   filter->motioncellscolor->G_channel_value = 255;
356   filter->motioncellscolor->B_channel_value = 0;
357   filter->motioncellsidx = NULL;
358   filter->motioncells_count = 0;
359   filter->motion_begin_timestamp = 0;
360   filter->last_motion_timestamp = 0;
361   filter->last_nomotion_notified = 0;
362   filter->consecutive_motion = 0;
363   filter->motion_timestamp = 0;
364   filter->prev_buff_timestamp = 0;
365   filter->cur_buff_timestamp = 0;
366   filter->diff_timestamp = -1;
367   filter->starttime = 1000 * g_get_real_time();
368   filter->previous_motion = FALSE;
369   filter->changed_datafile = FALSE;
370   filter->postallmotion = FALSE;
371   filter->usealpha = TRUE;
372   filter->firstdatafile = FALSE;
373   filter->firstgridx = TRUE;
374   filter->firstgridy = TRUE;
375   filter->changed_gridx = FALSE;
376   filter->changed_gridy = FALSE;
377   filter->firstframe = TRUE;
378   filter->changed_startime = FALSE;
379   filter->sent_init_error_msg = FALSE;
380   filter->sent_save_error_msg = FALSE;
381   filter->thickness = THICKNESS_DEF;
382 
383   filter->datafileidx = 0;
384   filter->id = motion_cells_init ();
385 
386   gst_opencv_video_filter_set_in_place (GST_OPENCV_VIDEO_FILTER_CAST (filter),
387       TRUE);
388 }
389 
390 static void
fix_coords(motionmaskcoordrect & coords,int width,int height)391 fix_coords (motionmaskcoordrect & coords, int width, int height)
392 {
393   --width;
394   --height;
395 
396   if (width < coords.upper_left_x)
397     coords.upper_left_x = width;
398   if (width < coords.lower_right_x)
399     coords.lower_right_x = width;
400 
401   if (height < coords.upper_left_y)
402     coords.upper_left_y = height;
403   if (height < coords.lower_right_y)
404     coords.lower_right_y = height;
405 }
406 
407 static void
gst_motion_cells_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)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 
423   GST_OBJECT_LOCK (filter);
424   switch (prop_id) {
425     case PROP_GRID_X:
426       filter->gridx = g_value_get_int (value);
427       if (filter->prevgridx != filter->gridx && !filter->firstframe) {
428         filter->changed_gridx = TRUE;
429       }
430       filter->prevgridx = filter->gridx;
431       break;
432     case PROP_GRID_Y:
433       filter->gridy = g_value_get_int (value);
434       if (filter->prevgridy != filter->gridy && !filter->firstframe) {
435         filter->changed_gridy = TRUE;
436       }
437       filter->prevgridy = filter->gridy;
438       break;
439     case PROP_GAP:
440       filter->gap = g_value_get_int (value);
441       break;
442     case PROP_POSTNOMOTION:
443       filter->postnomotion = g_value_get_int (value);
444       break;
445     case PROP_MINIMUNMOTIONFRAMES:
446       filter->minimum_motion_frames = g_value_get_int (value);
447       break;
448     case PROP_SENSITIVITY:
449       filter->sensitivity = g_value_get_double (value);
450       break;
451     case PROP_THRESHOLD:
452       filter->threshold = g_value_get_double (value);
453       break;
454     case PROP_DISPLAY:
455       filter->display = g_value_get_boolean (value);
456       break;
457     case PROP_POSTALLMOTION:
458       filter->postallmotion = g_value_get_boolean (value);
459       break;
460     case PROP_USEALPHA:
461       filter->usealpha = g_value_get_boolean (value);
462       break;
463     case PROP_CALCULATEMOTION:
464       filter->calculate_motion = g_value_get_boolean (value);
465       break;
466     case PROP_DATE:
467       if (!filter->firstframe) {
468         filter->changed_startime = TRUE;
469       }
470       filter->starttime = g_value_get_long (value);
471       break;
472     case PROP_DATAFILE:
473       GFREE (filter->cur_datafile);
474       GFREE (filter->basename_datafile);
475       filter->basename_datafile = g_value_dup_string (value);
476 
477       if (strlen (filter->basename_datafile) == 0) {
478         filter->cur_datafile = NULL;
479         break;
480       }
481       filter->cur_datafile =
482           g_strdup_printf ("%s-0.%s", filter->basename_datafile,
483           filter->datafile_extension);
484       if (g_strcmp0 (filter->prev_datafile, filter->basename_datafile) != 0) {
485         filter->changed_datafile = TRUE;
486         filter->sent_init_error_msg = FALSE;
487         filter->sent_save_error_msg = FALSE;
488         filter->datafileidx = 0;
489         motion_cells_free_resources (filter->id);
490       } else {
491         filter->changed_datafile = FALSE;
492       }
493 
494       GFREE (filter->prev_datafile);
495       filter->prev_datafile = g_strdup (filter->basename_datafile);
496       break;
497     case PROP_DATAFILE_EXT:
498       GFREE (filter->datafile_extension);
499       filter->datafile_extension = g_value_dup_string (value);
500       break;
501     case PROP_MOTIONMASKCOORD:
502       filter->has_delayed_mask = (0 < filter->width && 0 < filter->height);
503 
504       strs = g_strsplit (g_value_get_string (value), ",", 255);
505       GFREE (filter->motionmaskcoords);
506       //setting number of regions
507       for (filter->motionmaskcoord_count = 0;
508           strs[filter->motionmaskcoord_count] != NULL;
509           ++filter->motionmaskcoord_count);
510       if (filter->motionmaskcoord_count > 0) {
511         sscanf (strs[0], "%d:%d:%d:%d", &tmpux, &tmpuy, &tmplx, &tmply);
512         if (tmpux > -1 && tmpuy > -1 && tmplx > -1 && tmply > -1) {
513           filter->motionmaskcoords =
514               g_new0 (motionmaskcoordrect, filter->motionmaskcoord_count);
515 
516           for (i = 0; i < filter->motionmaskcoord_count; ++i) {
517             sscanf (strs[i], "%d:%d:%d:%d", &ux, &uy, &lx, &ly);
518 
519             filter->motionmaskcoords[i].upper_left_x = ux < 0 ? 0 : ux;
520             filter->motionmaskcoords[i].upper_left_y = uy < 0 ? 0 : uy;
521             filter->motionmaskcoords[i].lower_right_x = lx < 0 ? 0 : lx;
522             filter->motionmaskcoords[i].lower_right_y = ly < 0 ? 0 : ly;
523 
524             if (0 < filter->width && 0 < filter->height) {
525               fix_coords (filter->motionmaskcoords[i], filter->width,
526                   filter->height);
527             }
528           }
529         } else {
530           filter->motionmaskcoord_count = 0;
531         }
532       }
533       g_strfreev (strs);
534       tmpux = -1;
535       tmpuy = -1;
536       tmplx = -1;
537       tmply = -1;
538       break;
539     case PROP_MOTIONMASKCELLSPOS:
540       motionmaskcellsstr = g_strsplit (g_value_get_string (value), ",", 255);
541       GFREE (filter->motionmaskcellsidx);
542       //setting number of regions
543       for (filter->motionmaskcells_count = 0;
544           motionmaskcellsstr[filter->motionmaskcells_count] != NULL;
545           ++filter->motionmaskcells_count);
546       if (filter->motionmaskcells_count > 0) {
547         sscanf (motionmaskcellsstr[0], "%d:%d", &tmpux, &tmpuy);
548         if (tmpux > -1 && tmpuy > -1) {
549           filter->motionmaskcellsidx =
550               g_new0 (motioncellidx, filter->motionmaskcells_count);
551           for (i = 0; i < filter->motionmaskcells_count; ++i) {
552             sscanf (motionmaskcellsstr[i], "%d:%d", &masklinidx, &maskcolidx);
553             filter->motionmaskcellsidx[i].lineidx = masklinidx;
554             filter->motionmaskcellsidx[i].columnidx = maskcolidx;
555           }
556         } else {
557           filter->motionmaskcells_count = 0;
558         }
559       }
560       g_strfreev (motionmaskcellsstr);
561       tmpux = -1;
562       tmpuy = -1;
563       tmplx = -1;
564       tmply = -1;
565       break;
566     case PROP_CELLSCOLOR:
567       colorstr = g_strsplit (g_value_get_string (value), ",", 4);
568       for (cellscolorscnt = 0; colorstr[cellscolorscnt] != NULL;
569           ++cellscolorscnt);
570       if (cellscolorscnt != 3) {
571         GST_WARNING_OBJECT (filter, "Ignoring badly-formatted cellscolor RGB "
572             "string");
573       } else {
574         sscanf (colorstr[0], "%d", &r);
575         sscanf (colorstr[1], "%d", &g);
576         sscanf (colorstr[2], "%d", &b);
577         //check right RGB color format
578         r = CLAMP (r, 1, 255);
579         g = CLAMP (g, 1, 255);
580         b = CLAMP (b, 1, 255);
581         filter->motioncellscolor->R_channel_value = r;
582         filter->motioncellscolor->G_channel_value = g;
583         filter->motioncellscolor->B_channel_value = b;
584       }
585       g_strfreev (colorstr);
586       break;
587     case PROP_MOTIONCELLSIDX:
588       motioncellsstr = g_strsplit (g_value_get_string (value), ",", 255);
589 
590       //setting number of regions
591       for (filter->motioncells_count = 0;
592           motioncellsstr[filter->motioncells_count] != NULL;
593           ++filter->motioncells_count);
594       if (filter->motioncells_count > 0) {
595         sscanf (motioncellsstr[0], "%d:%d", &tmpux, &tmpuy);
596         if (tmpux > -1 && tmpuy > -1) {
597           GFREE (filter->motioncellsidx);
598 
599           filter->motioncellsidx =
600               g_new0 (motioncellidx, filter->motioncells_count);
601 
602           for (i = 0; i < filter->motioncells_count; ++i) {
603             sscanf (motioncellsstr[i], "%d:%d", &linidx, &colidx);
604             filter->motioncellsidx[i].lineidx = linidx;
605             filter->motioncellsidx[i].columnidx = colidx;
606           }
607         } else {
608           filter->motioncells_count = 0;
609         }
610       }
611       g_strfreev (motioncellsstr);
612       tmpux = -1;
613       tmpuy = -1;
614       tmplx = -1;
615       tmply = -1;
616       break;
617     case PROP_MOTIONCELLTHICKNESS:
618       filter->thickness = g_value_get_int (value);
619       break;
620     default:
621       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
622       break;
623   }
624   GST_OBJECT_UNLOCK (filter);
625 }
626 
627 static void
gst_motion_cells_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)628 gst_motion_cells_get_property (GObject * object, guint prop_id,
629     GValue * value, GParamSpec * pspec)
630 {
631   GstMotioncells *filter = gst_motion_cells (object);
632   GString *str;
633   int i;
634 
635   GST_OBJECT_LOCK (filter);
636   switch (prop_id) {
637     case PROP_GRID_X:
638       g_value_set_int (value, filter->gridx);
639       break;
640     case PROP_GRID_Y:
641       g_value_set_int (value, filter->gridy);
642       break;
643     case PROP_GAP:
644       g_value_set_int (value, filter->gap);
645       break;
646     case PROP_POSTNOMOTION:
647       g_value_set_int (value, filter->postnomotion);
648       break;
649     case PROP_MINIMUNMOTIONFRAMES:
650       g_value_set_int (value, filter->minimum_motion_frames);
651       break;
652     case PROP_SENSITIVITY:
653       g_value_set_double (value, filter->sensitivity);
654       break;
655     case PROP_THRESHOLD:
656       g_value_set_double (value, filter->threshold);
657       break;
658     case PROP_DISPLAY:
659       g_value_set_boolean (value, filter->display);
660       break;
661     case PROP_POSTALLMOTION:
662       g_value_set_boolean (value, filter->postallmotion);
663       break;
664     case PROP_USEALPHA:
665       g_value_set_boolean (value, filter->usealpha);
666       break;
667     case PROP_CALCULATEMOTION:
668       g_value_set_boolean (value, filter->calculate_motion);
669       break;
670     case PROP_DATE:
671       g_value_set_long (value, filter->starttime);
672       break;
673     case PROP_DATAFILE:
674       g_value_set_string (value, filter->basename_datafile);
675       break;
676     case PROP_DATAFILE_EXT:
677       g_value_set_string (value, filter->datafile_extension);
678       break;
679     case PROP_MOTIONMASKCOORD:
680       str = g_string_new ("");
681       for (i = 0; i < filter->motionmaskcoord_count; ++i) {
682         if (i < filter->motionmaskcoord_count - 1)
683           g_string_append_printf (str, "%d:%d:%d:%d,",
684               filter->motionmaskcoords[i].upper_left_x,
685               filter->motionmaskcoords[i].upper_left_y,
686               filter->motionmaskcoords[i].lower_right_x,
687               filter->motionmaskcoords[i].lower_right_y);
688         else
689           g_string_append_printf (str, "%d:%d:%d:%d",
690               filter->motionmaskcoords[i].upper_left_x,
691               filter->motionmaskcoords[i].upper_left_y,
692               filter->motionmaskcoords[i].lower_right_x,
693               filter->motionmaskcoords[i].lower_right_y);
694 
695       }
696       g_value_set_string (value, str->str);
697       g_string_free (str, TRUE);
698       break;
699     case PROP_MOTIONMASKCELLSPOS:
700       str = g_string_new ("");
701       for (i = 0; i < filter->motionmaskcells_count; ++i) {
702         if (i < filter->motionmaskcells_count - 1)
703           g_string_append_printf (str, "%d:%d,",
704               filter->motionmaskcellsidx[i].lineidx,
705               filter->motionmaskcellsidx[i].columnidx);
706         else
707           g_string_append_printf (str, "%d:%d",
708               filter->motionmaskcellsidx[i].lineidx,
709               filter->motionmaskcellsidx[i].columnidx);
710       }
711       g_value_set_string (value, str->str);
712       g_string_free (str, TRUE);
713       break;
714     case PROP_CELLSCOLOR:
715       str = g_string_new ("");
716 
717       g_string_printf (str, "%d,%d,%d",
718           filter->motioncellscolor->R_channel_value,
719           filter->motioncellscolor->G_channel_value,
720           filter->motioncellscolor->B_channel_value);
721 
722       g_value_set_string (value, str->str);
723       g_string_free (str, TRUE);
724       break;
725     case PROP_MOTIONCELLSIDX:
726       str = g_string_new ("");
727       for (i = 0; i < filter->motioncells_count; ++i) {
728         if (i < filter->motioncells_count - 1)
729           g_string_append_printf (str, "%d:%d,",
730               filter->motioncellsidx[i].lineidx,
731               filter->motioncellsidx[i].columnidx);
732         else
733           g_string_append_printf (str, "%d:%d",
734               filter->motioncellsidx[i].lineidx,
735               filter->motioncellsidx[i].columnidx);
736       }
737       g_value_set_string (value, str->str);
738       g_string_free (str, TRUE);
739       break;
740     case PROP_MOTIONCELLTHICKNESS:
741       g_value_set_int (value, filter->thickness);
742       break;
743     default:
744       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
745       break;
746   }
747   GST_OBJECT_UNLOCK (filter);
748 }
749 
750 static void
gst_motioncells_update_motion_cells(GstMotioncells * filter)751 gst_motioncells_update_motion_cells (GstMotioncells * filter)
752 {
753   int i = 0;
754   int cellscnt = 0;
755   int j = 0;
756   int newcellscnt;
757   motioncellidx *motioncellsidx;
758   for (i = 0; i < filter->motioncells_count; i++) {
759     if ((filter->gridx <= filter->motioncellsidx[i].columnidx) ||
760         (filter->gridy <= filter->motioncellsidx[i].lineidx)) {
761       cellscnt++;
762     }
763   }
764   newcellscnt = filter->motioncells_count - cellscnt;
765   motioncellsidx = g_new0 (motioncellidx, newcellscnt);
766   for (i = 0; i < filter->motioncells_count; i++) {
767     if ((filter->motioncellsidx[i].lineidx < filter->gridy) &&
768         (filter->motioncellsidx[i].columnidx < filter->gridx)) {
769       motioncellsidx[j].lineidx = filter->motioncellsidx[i].lineidx;
770       motioncellsidx[j].columnidx = filter->motioncellsidx[i].columnidx;
771       j++;
772     }
773   }
774   GFREE (filter->motioncellsidx);
775   filter->motioncells_count = newcellscnt;
776   filter->motioncellsidx = g_new0 (motioncellidx, filter->motioncells_count);
777   j = 0;
778   for (i = 0; i < filter->motioncells_count; i++) {
779     filter->motioncellsidx[i].lineidx = motioncellsidx[j].lineidx;
780     filter->motioncellsidx[i].columnidx = motioncellsidx[j].columnidx;
781     j++;
782   }
783   GFREE (motioncellsidx);
784 }
785 
786 static void
gst_motioncells_update_motion_masks(GstMotioncells * filter)787 gst_motioncells_update_motion_masks (GstMotioncells * filter)
788 {
789   int i = 0;
790   int maskcnt = 0;
791   int j = 0;
792   int newmaskcnt;
793   motioncellidx *motionmaskcellsidx;
794   for (i = 0; i < filter->motionmaskcells_count; i++) {
795     if ((filter->gridx <= filter->motionmaskcellsidx[i].columnidx) ||
796         (filter->gridy <= filter->motionmaskcellsidx[i].lineidx)) {
797       maskcnt++;
798     }
799   }
800   newmaskcnt = filter->motionmaskcells_count - maskcnt;
801   motionmaskcellsidx = g_new0 (motioncellidx, newmaskcnt);
802   for (i = 0; i < filter->motionmaskcells_count; i++) {
803     if ((filter->motionmaskcellsidx[i].lineidx < filter->gridy) &&
804         (filter->motionmaskcellsidx[i].columnidx < filter->gridx)) {
805       motionmaskcellsidx[j].lineidx = filter->motionmaskcellsidx[i].lineidx;
806       motionmaskcellsidx[j].columnidx = filter->motionmaskcellsidx[i].columnidx;
807       j++;
808     }
809   }
810   GFREE (filter->motionmaskcellsidx);
811   filter->motionmaskcells_count = newmaskcnt;
812   filter->motionmaskcellsidx =
813       g_new0 (motioncellidx, filter->motionmaskcells_count);
814   j = 0;
815   for (i = 0; i < filter->motionmaskcells_count; i++) {
816     filter->motionmaskcellsidx[i].lineidx = motionmaskcellsidx[j].lineidx;
817     filter->motionmaskcellsidx[i].columnidx = motionmaskcellsidx[j].columnidx;
818     j++;
819   }
820   GFREE (motionmaskcellsidx);
821 }
822 
823 /* GstElement vmethod implementations */
824 
825 /* this function handles the link with other elements */
826 static gboolean
gst_motion_cells_handle_sink_event(GstPad * pad,GstObject * parent,GstEvent * event)827 gst_motion_cells_handle_sink_event (GstPad * pad, GstObject * parent,
828     GstEvent * event)
829 {
830   GstMotioncells *filter;
831   GstVideoInfo info;
832   gboolean res = TRUE;
833   int i;
834 
835   filter = gst_motion_cells (parent);
836 
837   switch (GST_EVENT_TYPE (event)) {
838     case GST_EVENT_CAPS:
839     {
840       GstCaps *caps;
841       gst_event_parse_caps (event, &caps);
842       gst_video_info_from_caps (&info, caps);
843 
844       filter->width = info.width;
845       filter->height = info.height;
846 
847       if (0 != filter->has_delayed_mask
848           && 0 < filter->motionmaskcoord_count
849           && NULL != filter->motionmaskcoords && 0 < filter->width
850           && 0 < filter->height) {
851         filter->has_delayed_mask = 0;
852         for (i = 0; i < filter->motionmaskcoord_count; ++i) {
853           fix_coords (filter->motionmaskcoords[i], filter->width,
854               filter->height);
855         }
856       }
857 
858       filter->framerate = (double) info.fps_n / (double) info.fps_d;
859       break;
860     }
861     default:
862       break;
863   }
864 
865   res = gst_pad_event_default (pad, parent, event);
866 
867   return res;
868 }
869 
870 /* chain function
871  * this function does the actual processing
872  */
873 static GstFlowReturn
gst_motion_cells_transform_ip(GstOpencvVideoFilter * base,GstBuffer * buf,cv::Mat img)874 gst_motion_cells_transform_ip (GstOpencvVideoFilter * base, GstBuffer * buf,
875     cv::Mat img)
876 {
877   GstMotioncells *filter = gst_motion_cells (base);
878 
879   GST_OBJECT_LOCK (filter);
880   if (filter->calculate_motion) {
881     double sensitivity;
882     int framerate, gridx, gridy, motionmaskcells_count, motionmaskcoord_count,
883         motioncells_count, i;
884     int thickness, success, motioncellsidxcnt, numberOfCells,
885         motioncellsnumber, cellsOfInterestNumber;
886     int mincellsOfInterestNumber, motiondetect;
887     guint minimum_motion_frames, postnomotion;
888     char *datafile;
889     bool display, changed_datafile, useAlpha;
890     gint64 starttime;
891     motionmaskcoordrect *motionmaskcoords;
892     motioncellidx *motionmaskcellsidx;
893     cellscolor motioncellscolor;
894     motioncellidx *motioncellsidx;
895 
896     if (filter->firstframe) {
897       setPrevFrame (img, filter->id);
898       filter->firstframe = FALSE;
899     }
900 
901     minimum_motion_frames = filter->minimum_motion_frames;
902     postnomotion = filter->postnomotion;
903     sensitivity = filter->sensitivity;
904     framerate = filter->framerate;
905     gridx = filter->gridx;
906     gridy = filter->gridy;
907     display = filter->display;
908     motionmaskcoord_count = filter->motionmaskcoord_count;
909     motionmaskcoords =
910         g_new0 (motionmaskcoordrect, filter->motionmaskcoord_count);
911     for (i = 0; i < filter->motionmaskcoord_count; i++) {       //we need divide 2 because we use gauss pyramid in C++ side
912       motionmaskcoords[i].upper_left_x =
913           filter->motionmaskcoords[i].upper_left_x / 2;
914       motionmaskcoords[i].upper_left_y =
915           filter->motionmaskcoords[i].upper_left_y / 2;
916       motionmaskcoords[i].lower_right_x =
917           filter->motionmaskcoords[i].lower_right_x / 2;
918       motionmaskcoords[i].lower_right_y =
919           filter->motionmaskcoords[i].lower_right_y / 2;
920     }
921 
922     motioncellscolor.R_channel_value =
923         filter->motioncellscolor->R_channel_value;
924     motioncellscolor.G_channel_value =
925         filter->motioncellscolor->G_channel_value;
926     motioncellscolor.B_channel_value =
927         filter->motioncellscolor->B_channel_value;
928 
929     if ((filter->changed_gridx || filter->changed_gridy
930             || filter->changed_startime)) {
931       if ((g_strcmp0 (filter->cur_datafile, NULL) != 0)) {
932         GFREE (filter->cur_datafile);
933         filter->datafileidx++;
934         filter->cur_datafile =
935             g_strdup_printf ("%s-%d.%s", filter->basename_datafile,
936             filter->datafileidx, filter->datafile_extension);
937         filter->changed_datafile = TRUE;
938         motion_cells_free_resources (filter->id);
939       }
940       if (filter->motioncells_count > 0)
941         gst_motioncells_update_motion_cells (filter);
942       if (filter->motionmaskcells_count > 0)
943         gst_motioncells_update_motion_masks (filter);
944       filter->changed_gridx = FALSE;
945       filter->changed_gridy = FALSE;
946       filter->changed_startime = FALSE;
947     }
948 
949     datafile = g_strdup (filter->cur_datafile);
950     filter->cur_buff_timestamp = (GST_BUFFER_TIMESTAMP (buf) / GST_MSECOND);
951     filter->starttime +=
952         (filter->cur_buff_timestamp - filter->prev_buff_timestamp);
953     starttime = filter->starttime;
954     if (filter->changed_datafile || filter->diff_timestamp < 0)
955       filter->diff_timestamp =
956           (gint64) (GST_BUFFER_TIMESTAMP (buf) / GST_MSECOND);
957     changed_datafile = filter->changed_datafile;
958 
959     motionmaskcells_count = filter->motionmaskcells_count;
960     motionmaskcellsidx = g_new0 (motioncellidx, filter->motionmaskcells_count);
961     for (i = 0; i < filter->motionmaskcells_count; i++) {
962       motionmaskcellsidx[i].lineidx = filter->motionmaskcellsidx[i].lineidx;
963       motionmaskcellsidx[i].columnidx = filter->motionmaskcellsidx[i].columnidx;
964     }
965     motioncells_count = filter->motioncells_count;
966     motioncellsidx = g_new0 (motioncellidx, filter->motioncells_count);
967     for (i = 0; i < filter->motioncells_count; i++) {
968       motioncellsidx[i].lineidx = filter->motioncellsidx[i].lineidx;
969       motioncellsidx[i].columnidx = filter->motioncellsidx[i].columnidx;
970     }
971 
972     useAlpha = filter->usealpha;
973     thickness = filter->thickness;
974     success =
975         perform_detection_motion_cells (img, sensitivity,
976         framerate, gridx, gridy,
977         (gint64) (GST_BUFFER_TIMESTAMP (buf) / GST_MSECOND) -
978         filter->diff_timestamp, display, useAlpha, motionmaskcoord_count,
979         motionmaskcoords, motionmaskcells_count, motionmaskcellsidx,
980         motioncellscolor, motioncells_count, motioncellsidx, starttime,
981         datafile, changed_datafile, thickness, filter->id);
982 
983     if ((success == 1) && (filter->sent_init_error_msg == FALSE)) {
984       char *initfailedreason;
985       int initerrorcode;
986       GstStructure *s;
987       GstMessage *m;
988 
989       initfailedreason = getInitDataFileFailed (filter->id);
990       initerrorcode = getInitErrorCode (filter->id);
991       s = gst_structure_new ("motion", "init_error_code", G_TYPE_INT,
992           initerrorcode, "details", G_TYPE_STRING, initfailedreason, NULL);
993       m = gst_message_new_element (GST_OBJECT (filter), s);
994       gst_element_post_message (GST_ELEMENT (filter), m);
995       filter->sent_init_error_msg = TRUE;
996     }
997     if ((success == -1) && (filter->sent_save_error_msg == FALSE)) {
998       char *savefailedreason;
999       int saveerrorcode;
1000       GstStructure *s;
1001       GstMessage *m;
1002 
1003       savefailedreason = getSaveDataFileFailed (filter->id);
1004       saveerrorcode = getSaveErrorCode (filter->id);
1005       s = gst_structure_new ("motion", "save_error_code", G_TYPE_INT,
1006           saveerrorcode, "details", G_TYPE_STRING, savefailedreason, NULL);
1007       m = gst_message_new_element (GST_OBJECT (filter), s);
1008       gst_element_post_message (GST_ELEMENT (filter), m);
1009       filter->sent_save_error_msg = TRUE;
1010     }
1011     if (success == -2) {
1012       GST_LOG_OBJECT (filter, "frame dropped");
1013       filter->prev_buff_timestamp = filter->cur_buff_timestamp;
1014       //free
1015       GFREE (datafile);
1016       GFREE (motionmaskcoords);
1017       GFREE (motionmaskcellsidx);
1018       GFREE (motioncellsidx);
1019       GST_OBJECT_UNLOCK (filter);
1020       return GST_FLOW_OK;
1021     }
1022 
1023     filter->changed_datafile = getChangedDataFile (filter->id);
1024     motioncellsidxcnt = getMotionCellsIdxCnt (filter->id);
1025     numberOfCells = filter->gridx * filter->gridy;
1026     motioncellsnumber = motioncellsidxcnt / MSGLEN;
1027     cellsOfInterestNumber = (filter->motioncells_count > 0) ?   //how many cells interest for us
1028         (filter->motioncells_count) : (numberOfCells);
1029     mincellsOfInterestNumber =
1030         floor ((double) cellsOfInterestNumber * filter->threshold);
1031     GST_OBJECT_UNLOCK (filter);
1032     motiondetect = (motioncellsnumber >= mincellsOfInterestNumber) ? 1 : 0;
1033     if ((motioncellsidxcnt > 0) && (motiondetect == 1)) {
1034       char *detectedmotioncells;
1035 
1036       filter->last_motion_timestamp = GST_BUFFER_TIMESTAMP (buf);
1037       detectedmotioncells = getMotionCellsIdx (filter->id);
1038       if (detectedmotioncells) {
1039         filter->consecutive_motion++;
1040         if ((filter->previous_motion == FALSE)
1041             && (filter->consecutive_motion >= minimum_motion_frames)) {
1042           GstStructure *s;
1043           GstMessage *m;
1044 
1045           GST_DEBUG_OBJECT (filter, "motion started, post msg on the bus");
1046           filter->previous_motion = TRUE;
1047           filter->motion_begin_timestamp = GST_BUFFER_TIMESTAMP (buf);
1048           s = gst_structure_new ("motion", "motion_cells_indices",
1049               G_TYPE_STRING, detectedmotioncells, "motion_begin",
1050               G_TYPE_UINT64, filter->motion_begin_timestamp, NULL);
1051           m = gst_message_new_element (GST_OBJECT (filter), s);
1052           gst_element_post_message (GST_ELEMENT (filter), m);
1053         } else if (filter->postallmotion) {
1054           GstStructure *s;
1055           GstMessage *m;
1056 
1057           GST_DEBUG_OBJECT (filter, "motion, post msg on the bus");
1058           filter->motion_timestamp = GST_BUFFER_TIMESTAMP (buf);
1059           s = gst_structure_new ("motion", "motion_cells_indices",
1060               G_TYPE_STRING, detectedmotioncells, "motion", G_TYPE_UINT64,
1061               filter->motion_timestamp, NULL);
1062           m = gst_message_new_element (GST_OBJECT (filter), s);
1063           gst_element_post_message (GST_ELEMENT (filter), m);
1064         }
1065       } else {
1066         GstStructure *s;
1067         GstMessage *m;
1068 
1069         s = gst_structure_new ("motion", "motion_cells_indices",
1070             G_TYPE_STRING, "error", NULL);
1071         m = gst_message_new_element (GST_OBJECT (filter), s);
1072         gst_element_post_message (GST_ELEMENT (filter), m);
1073       }
1074     } else {
1075       filter->consecutive_motion = 0;
1076       if ((((GST_BUFFER_TIMESTAMP (buf) -
1077                       filter->last_motion_timestamp) / 1000000000l) >=
1078               filter->gap)
1079           && (filter->last_motion_timestamp > 0)) {
1080         if (filter->previous_motion) {
1081           GstStructure *s;
1082           GstMessage *m;
1083 
1084           GST_DEBUG_OBJECT (filter, "motion finished, post msg on the bus");
1085           filter->previous_motion = FALSE;
1086           s = gst_structure_new ("motion", "motion_finished", G_TYPE_UINT64,
1087               filter->last_motion_timestamp, NULL);
1088           m = gst_message_new_element (GST_OBJECT (filter), s);
1089           gst_element_post_message (GST_ELEMENT (filter), m);
1090         }
1091       }
1092     }
1093     if (postnomotion > 0) {
1094       guint64 last_buf_timestamp = GST_BUFFER_TIMESTAMP (buf) / 1000000000l;
1095       if ((last_buf_timestamp -
1096               (filter->last_motion_timestamp / 1000000000l)) >=
1097           filter->postnomotion) {
1098         GST_DEBUG_OBJECT (filter, "post no motion msg on the bus");
1099         if ((last_buf_timestamp -
1100                 (filter->last_nomotion_notified / 1000000000l)) >=
1101             filter->postnomotion) {
1102           GstStructure *s;
1103           GstMessage *m;
1104 
1105           filter->last_nomotion_notified = GST_BUFFER_TIMESTAMP (buf);
1106           s = gst_structure_new ("motion", "no_motion", G_TYPE_UINT64,
1107               filter->last_motion_timestamp, NULL);
1108           m = gst_message_new_element (GST_OBJECT (filter), s);
1109           gst_element_post_message (GST_ELEMENT (filter), m);
1110         }
1111       }
1112     }
1113     filter->prev_buff_timestamp = filter->cur_buff_timestamp;
1114 
1115     //free
1116     GFREE (datafile);
1117     GFREE (motionmaskcoords);
1118     GFREE (motionmaskcellsidx);
1119     GFREE (motioncellsidx);
1120   } else {
1121     GST_WARNING_OBJECT (filter, "Motion detection disabled");
1122     GST_OBJECT_UNLOCK (filter);
1123   }
1124   return GST_FLOW_OK;
1125 }
1126