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