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