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