LCOV - code coverage report
Current view: top level - gst-plugins-bad/gst/fieldanalysis - gstfieldanalysis.c (source / functions) Hit Total Coverage
Test: GStreamer Lines: 150 823 18.2 %
Date: 2011-03-25 Functions: 13 30 43.3 %
Branches: 35 524 6.7 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * GStreamer
       3                 :            :  * Copyright (C) 2011 Robert Swain <robert.swain@collabora.co.uk>
       4                 :            :  *
       5                 :            :  * Permission is hereby granted, free of charge, to any person obtaining a
       6                 :            :  * copy of this software and associated documentation files (the "Software"),
       7                 :            :  * to deal in the Software without restriction, including without limitation
       8                 :            :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
       9                 :            :  * and/or sell copies of the Software, and to permit persons to whom the
      10                 :            :  * Software is furnished to do so, subject to the following conditions:
      11                 :            :  *
      12                 :            :  * The above copyright notice and this permission notice shall be included in
      13                 :            :  * all copies or substantial portions of the Software.
      14                 :            :  *
      15                 :            :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      16                 :            :  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      17                 :            :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      18                 :            :  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      19                 :            :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      20                 :            :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      21                 :            :  * DEALINGS IN THE SOFTWARE.
      22                 :            :  *
      23                 :            :  * Alternatively, the contents of this file may be used under the
      24                 :            :  * GNU Lesser General Public License Version 2.1 (the "LGPL"), in
      25                 :            :  * which case the following provisions apply instead of the ones
      26                 :            :  * mentioned above:
      27                 :            :  *
      28                 :            :  * This library is free software; you can redistribute it and/or
      29                 :            :  * modify it under the terms of the GNU Library General Public
      30                 :            :  * License as published by the Free Software Foundation; either
      31                 :            :  * version 2 of the License, or (at your option) any later version.
      32                 :            :  *
      33                 :            :  * This library is distributed in the hope that it will be useful,
      34                 :            :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      35                 :            :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      36                 :            :  * Library General Public License for more details.
      37                 :            :  *
      38                 :            :  * You should have received a copy of the GNU Library General Public
      39                 :            :  * License along with this library; if not, write to the
      40                 :            :  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
      41                 :            :  * Boston, MA 02111-1307, USA.
      42                 :            :  */
      43                 :            : 
      44                 :            : /**
      45                 :            :  * SECTION:element-fieldanalysis
      46                 :            :  *
      47                 :            :  * Analyse fields from video buffers to identify whether the buffers are
      48                 :            :  * progressive/telecined/interlaced and, if telecined, the telecine pattern
      49                 :            :  * used.
      50                 :            :  *
      51                 :            :  * <refsect2>
      52                 :            :  * <title>Example launch line</title>
      53                 :            :  * |[
      54                 :            :  * gst-launch -v uridecodebin uri=/path/to/foo.bar ! fieldanalysis ! deinterlace ! ffmpegcolorspace ! autovideosink
      55                 :            :  * ]| This pipeline will analyse a video stream with default metrics and thresholds and output progressive frames.
      56                 :            :  * </refsect2>
      57                 :            :  */
      58                 :            : 
      59                 :            : #ifdef HAVE_CONFIG_H
      60                 :            : #  include <config.h>
      61                 :            : #endif
      62                 :            : 
      63                 :            : #include <gst/gst.h>
      64                 :            : #include <gst/video/video.h>
      65                 :            : #include <string.h>
      66                 :            : #include <stdlib.h>             /* for abs() */
      67                 :            : 
      68                 :            : #include "gstfieldanalysis.h"
      69                 :            : #include "gstfieldanalysisorc.h"
      70                 :            : 
      71                 :            : GST_DEBUG_CATEGORY_STATIC (gst_field_analysis_debug);
      72                 :            : #define GST_CAT_DEFAULT gst_field_analysis_debug
      73                 :            : 
      74                 :            : #define DEFAULT_FIELD_METRIC GST_FIELDANALYSIS_SSD
      75                 :            : #define DEFAULT_FRAME_METRIC GST_FIELDANALYSIS_5_TAP
      76                 :            : #define DEFAULT_NOISE_FLOOR 16
      77                 :            : #define DEFAULT_FIELD_THRESH 0.08f
      78                 :            : #define DEFAULT_FRAME_THRESH 0.002f
      79                 :            : #define DEFAULT_COMB_METHOD METHOD_5_TAP
      80                 :            : #define DEFAULT_SPATIAL_THRESH 9
      81                 :            : #define DEFAULT_BLOCK_WIDTH 16
      82                 :            : #define DEFAULT_BLOCK_HEIGHT 16
      83                 :            : #define DEFAULT_BLOCK_THRESH 80
      84                 :            : #define DEFAULT_IGNORED_LINES 2
      85                 :            : 
      86                 :            : enum
      87                 :            : {
      88                 :            :   PROP_0,
      89                 :            :   PROP_FIELD_METRIC,
      90                 :            :   PROP_FRAME_METRIC,
      91                 :            :   PROP_NOISE_FLOOR,
      92                 :            :   PROP_FIELD_THRESH,
      93                 :            :   PROP_FRAME_THRESH,
      94                 :            :   PROP_COMB_METHOD,
      95                 :            :   PROP_SPATIAL_THRESH,
      96                 :            :   PROP_BLOCK_WIDTH,
      97                 :            :   PROP_BLOCK_HEIGHT,
      98                 :            :   PROP_BLOCK_THRESH,
      99                 :            :   PROP_IGNORED_LINES
     100                 :            : };
     101                 :            : 
     102                 :            : static GstStaticPadTemplate sink_factory =
     103                 :            : GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
     104                 :            :     GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("{YUY2,UYVY,I420,YV12}")));
     105                 :            : 
     106                 :            : static GstStaticPadTemplate src_factory =
     107                 :            : GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
     108                 :            :     GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("{YUY2,UYVY,I420,YV12}")));
     109                 :            : 
     110         [ +  + ]:         61 : GST_BOILERPLATE (GstFieldAnalysis, gst_field_analysis, GstElement,
     111                 :         61 :     GST_TYPE_ELEMENT);
     112                 :            : 
     113                 :            : static void gst_field_analysis_set_property (GObject * object, guint prop_id,
     114                 :            :     const GValue * value, GParamSpec * pspec);
     115                 :            : static void gst_field_analysis_get_property (GObject * object, guint prop_id,
     116                 :            :     GValue * value, GParamSpec * pspec);
     117                 :            : 
     118                 :            : static gboolean gst_field_analysis_set_caps (GstPad * pad, GstCaps * caps);
     119                 :            : static gboolean gst_field_analysis_sink_event (GstPad * pad, GstEvent * event);
     120                 :            : static GstFlowReturn gst_field_analysis_chain (GstPad * pad, GstBuffer * buf);
     121                 :            : static GstStateChangeReturn gst_field_analysis_change_state (GstElement *
     122                 :            :     element, GstStateChange transition);
     123                 :            : static void gst_field_analysis_finalize (GObject * self);
     124                 :            : 
     125                 :            : static GQueue *gst_field_analysis_flush_queue (GstFieldAnalysis * filter,
     126                 :            :     GQueue * queue);
     127                 :            : 
     128                 :            : static void
     129                 :          6 : gst_field_analysis_base_init (gpointer gclass)
     130                 :            : {
     131                 :          6 :   GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
     132                 :            : 
     133                 :          6 :   gst_element_class_set_details_simple (element_class,
     134                 :            :       "Video field analysis",
     135                 :            :       "Filter/Analysis/Video",
     136                 :            :       "Analyse fields from video frames to identify if they are progressive/telecined/interlaced",
     137                 :            :       "Robert Swain <robert.swain@collabora.co.uk>");
     138                 :            : 
     139                 :          6 :   gst_element_class_add_pad_template (element_class,
     140                 :            :       gst_static_pad_template_get (&src_factory));
     141                 :          6 :   gst_element_class_add_pad_template (element_class,
     142                 :            :       gst_static_pad_template_get (&sink_factory));
     143                 :          6 : }
     144                 :            : 
     145                 :            : typedef enum
     146                 :            : {
     147                 :            :   GST_FIELDANALYSIS_SAD,
     148                 :            :   GST_FIELDANALYSIS_SSD,
     149                 :            :   GST_FIELDANALYSIS_3_TAP
     150                 :            : } GstFieldAnalysisFieldMetric;
     151                 :            : 
     152                 :            : #define GST_TYPE_FIELDANALYSIS_FIELD_METRIC (gst_fieldanalysis_field_metric_get_type())
     153                 :            : static GType
     154                 :          6 : gst_fieldanalysis_field_metric_get_type (void)
     155                 :            : {
     156                 :            :   static GType fieldanalysis_field_metric_type = 0;
     157                 :            : 
     158         [ +  - ]:          6 :   if (!fieldanalysis_field_metric_type) {
     159                 :            :     static const GEnumValue fieldanalysis_field_metrics[] = {
     160                 :            :       {GST_FIELDANALYSIS_SAD, "Sum of Absolute Differences", "sad"},
     161                 :            :       {GST_FIELDANALYSIS_SSD, "Sum of Squared Differences", "ssd"},
     162                 :            :       {GST_FIELDANALYSIS_3_TAP, "Difference of 3-tap [1,4,1] Horizontal Filter",
     163                 :            :           "3-tap"},
     164                 :            :       {0, NULL, NULL},
     165                 :            :     };
     166                 :            : 
     167                 :          6 :     fieldanalysis_field_metric_type =
     168                 :          6 :         g_enum_register_static ("GstFieldAnalysisFieldMetric",
     169                 :            :         fieldanalysis_field_metrics);
     170                 :            :   }
     171                 :            : 
     172                 :          6 :   return fieldanalysis_field_metric_type;
     173                 :            : }
     174                 :            : 
     175                 :            : typedef enum
     176                 :            : {
     177                 :            :   GST_FIELDANALYSIS_5_TAP,
     178                 :            :   GST_FIELDANALYSIS_WINDOWED_COMB
     179                 :            : } GstFieldAnalysisFrameMetric;
     180                 :            : 
     181                 :            : #define GST_TYPE_FIELDANALYSIS_FRAME_METRIC (gst_fieldanalysis_frame_metric_get_type())
     182                 :            : static GType
     183                 :          6 : gst_fieldanalysis_frame_metric_get_type (void)
     184                 :            : {
     185                 :            :   static GType fieldanalysis_frame_metric_type = 0;
     186                 :            : 
     187         [ +  - ]:          6 :   if (!fieldanalysis_frame_metric_type) {
     188                 :            :     static const GEnumValue fieldanalyis_frame_metrics[] = {
     189                 :            :       {GST_FIELDANALYSIS_5_TAP, "5-tap [1,-3,4,-3,1] Vertical Filter", "5-tap"},
     190                 :            :       {GST_FIELDANALYSIS_WINDOWED_COMB,
     191                 :            :             "Windowed Comb Detection (not optimised)",
     192                 :            :           "windowed-comb"},
     193                 :            :       {0, NULL, NULL},
     194                 :            :     };
     195                 :            : 
     196                 :          6 :     fieldanalysis_frame_metric_type =
     197                 :          6 :         g_enum_register_static ("GstFieldAnalysisFrameMetric",
     198                 :            :         fieldanalyis_frame_metrics);
     199                 :            :   }
     200                 :            : 
     201                 :          6 :   return fieldanalysis_frame_metric_type;
     202                 :            : }
     203                 :            : 
     204                 :            : #define GST_TYPE_FIELDANALYSIS_COMB_METHOD (gst_fieldanalysis_comb_method_get_type())
     205                 :            : static GType
     206                 :          6 : gst_fieldanalysis_comb_method_get_type (void)
     207                 :            : {
     208                 :            :   static GType fieldanalysis_comb_method_type = 0;
     209                 :            : 
     210         [ +  - ]:          6 :   if (!fieldanalysis_comb_method_type) {
     211                 :            :     static const GEnumValue fieldanalyis_comb_methods[] = {
     212                 :            :       {METHOD_32DETECT,
     213                 :            :             "Difference to above sample in same field small and difference to sample in other field large",
     214                 :            :           "32-detect"},
     215                 :            :       {METHOD_IS_COMBED,
     216                 :            :             "Differences between current sample and the above/below samples in other field multiplied together, larger than squared spatial threshold (from Tritical's isCombed)",
     217                 :            :           "isCombed"},
     218                 :            :       {METHOD_5_TAP,
     219                 :            :             "5-tap [1,-3,4,-3,1] vertical filter result is larger than spatial threshold*6",
     220                 :            :           "5-tap"},
     221                 :            :       {0, NULL, NULL},
     222                 :            :     };
     223                 :            : 
     224                 :          6 :     fieldanalysis_comb_method_type =
     225                 :          6 :         g_enum_register_static ("FieldAnalysisCombMethod",
     226                 :            :         fieldanalyis_comb_methods);
     227                 :            :   }
     228                 :            : 
     229                 :          6 :   return fieldanalysis_comb_method_type;
     230                 :            : }
     231                 :            : 
     232                 :            : static void
     233                 :          6 : gst_field_analysis_class_init (GstFieldAnalysisClass * klass)
     234                 :            : {
     235                 :            :   GObjectClass *gobject_class;
     236                 :            :   GstElementClass *gstelement_class;
     237                 :            : 
     238                 :          6 :   gobject_class = (GObjectClass *) klass;
     239                 :          6 :   gstelement_class = (GstElementClass *) klass;
     240                 :            : 
     241                 :          6 :   gobject_class->set_property = gst_field_analysis_set_property;
     242                 :          6 :   gobject_class->get_property = gst_field_analysis_get_property;
     243                 :          6 :   gobject_class->finalize = gst_field_analysis_finalize;
     244                 :            : 
     245                 :          6 :   g_object_class_install_property (gobject_class, PROP_FIELD_METRIC,
     246                 :            :       g_param_spec_enum ("field-metric", "Field Metric",
     247                 :            :           "Metric to be used for comparing same parity fields to decide if they are a repeated field for telecine",
     248                 :            :           GST_TYPE_FIELDANALYSIS_FIELD_METRIC, DEFAULT_FIELD_METRIC,
     249                 :            :           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
     250                 :          6 :   g_object_class_install_property (gobject_class, PROP_FRAME_METRIC,
     251                 :            :       g_param_spec_enum ("frame-metric", "Frame Metric",
     252                 :            :           "Metric to be used for comparing opposite parity fields to decide if they are a progressive frame",
     253                 :            :           GST_TYPE_FIELDANALYSIS_FRAME_METRIC, DEFAULT_FRAME_METRIC,
     254                 :            :           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
     255                 :          6 :   g_object_class_install_property (gobject_class, PROP_NOISE_FLOOR,
     256                 :            :       g_param_spec_uint ("noise-floor", "Noise Floor",
     257                 :            :           "Noise floor for appropriate metrics (per-pixel metric values with a score less than this will be ignored)",
     258                 :            :           0, G_MAXUINT32,
     259                 :            :           DEFAULT_NOISE_FLOOR, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
     260                 :          6 :   g_object_class_install_property (gobject_class, PROP_FIELD_THRESH,
     261                 :            :       g_param_spec_float ("field-threshold", "Field Threshold",
     262                 :            :           "Threshold for field metric decisions", 0.0f, G_MAXFLOAT,
     263                 :            :           DEFAULT_FIELD_THRESH, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
     264                 :          6 :   g_object_class_install_property (gobject_class, PROP_FRAME_THRESH,
     265                 :            :       g_param_spec_float ("frame-threshold", "Frame Threshold",
     266                 :            :           "Threshold for frame metric decisions", 0.0f, G_MAXFLOAT,
     267                 :            :           DEFAULT_FRAME_THRESH, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
     268                 :          6 :   g_object_class_install_property (gobject_class, PROP_COMB_METHOD,
     269                 :            :       g_param_spec_enum ("comb-method", "Comb-detection Method",
     270                 :            :           "Metric to be used for identifying comb artifacts if using windowed comb detection",
     271                 :            :           GST_TYPE_FIELDANALYSIS_COMB_METHOD, DEFAULT_COMB_METHOD,
     272                 :            :           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
     273                 :          6 :   g_object_class_install_property (gobject_class, PROP_SPATIAL_THRESH,
     274                 :            :       g_param_spec_int64 ("spatial-threshold", "Spatial Combing Threshold",
     275                 :            :           "Threshold for combing metric decisions", 0, G_MAXINT64,
     276                 :            :           DEFAULT_SPATIAL_THRESH, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
     277                 :          6 :   g_object_class_install_property (gobject_class, PROP_BLOCK_WIDTH,
     278                 :            :       g_param_spec_uint64 ("block-width", "Block width",
     279                 :            :           "Block width for windowed comb detection", 0, G_MAXUINT64,
     280                 :            :           DEFAULT_BLOCK_WIDTH, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
     281                 :          6 :   g_object_class_install_property (gobject_class, PROP_BLOCK_HEIGHT,
     282                 :            :       g_param_spec_uint64 ("block-height", "Block height",
     283                 :            :           "Block height for windowed comb detection", 0, G_MAXUINT64,
     284                 :            :           DEFAULT_BLOCK_HEIGHT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
     285                 :          6 :   g_object_class_install_property (gobject_class, PROP_BLOCK_THRESH,
     286                 :            :       g_param_spec_uint64 ("block-threshold", "Block threshold",
     287                 :            :           "Block threshold for windowed comb detection", 0, G_MAXUINT64,
     288                 :            :           DEFAULT_BLOCK_THRESH, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
     289                 :          6 :   g_object_class_install_property (gobject_class, PROP_IGNORED_LINES,
     290                 :            :       g_param_spec_uint64 ("ignored-lines", "Ignored lines",
     291                 :            :           "Ignore this many lines from the top and bottom for windowed comb detection",
     292                 :            :           2, G_MAXUINT64, DEFAULT_IGNORED_LINES,
     293                 :            :           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
     294                 :            : 
     295                 :          6 :   gstelement_class->change_state =
     296                 :          6 :       GST_DEBUG_FUNCPTR (gst_field_analysis_change_state);
     297                 :          6 : }
     298                 :            : 
     299                 :            : static gfloat same_parity_sad (GstFieldAnalysis * filter,
     300                 :            :     FieldAnalysisFields * fields);
     301                 :            : static gfloat same_parity_ssd (GstFieldAnalysis * filter,
     302                 :            :     FieldAnalysisFields * fields);
     303                 :            : static gfloat same_parity_3_tap (GstFieldAnalysis * filter,
     304                 :            :     FieldAnalysisFields * fields);
     305                 :            : static gfloat opposite_parity_5_tap (GstFieldAnalysis * filter,
     306                 :            :     FieldAnalysisFields * fields);
     307                 :            : static guint64 block_score_for_row_32detect (GstFieldAnalysis * filter,
     308                 :            :     guint8 * base_fj, guint8 * base_fjp1);
     309                 :            : static guint64 block_score_for_row_iscombed (GstFieldAnalysis * filter,
     310                 :            :     guint8 * base_fj, guint8 * base_fjp1);
     311                 :            : static guint64 block_score_for_row_5_tap (GstFieldAnalysis * filter,
     312                 :            :     guint8 * base_fj, guint8 * base_fjp1);
     313                 :            : static gfloat opposite_parity_windowed_comb (GstFieldAnalysis * filter,
     314                 :            :     FieldAnalysisFields * fields);
     315                 :            : 
     316                 :            : 
     317                 :            : static void
     318                 :         15 : gst_field_analysis_reset (GstFieldAnalysis * filter)
     319                 :            : {
     320         [ +  - ]:         15 :   if (filter->frames) {
     321                 :         15 :     guint length = g_queue_get_length (filter->frames);
     322         [ -  + ]:         15 :     GST_DEBUG_OBJECT (filter, "Clearing queue (size %u)", length);
     323         [ -  + ]:         15 :     while (length) {
     324                 :            :       /* each buffer in the queue should have a ref on it and so to clear the
     325                 :            :        * queue we must pop and unref each buffer here */
     326                 :          0 :       gst_buffer_unref (g_queue_pop_head (filter->frames));
     327                 :          0 :       length--;
     328                 :            :     }
     329                 :            :   }
     330         [ -  + ]:         15 :   GST_DEBUG_OBJECT (filter, "Resetting context");
     331                 :         15 :   memset (filter->results, 0, 2 * sizeof (FieldAnalysis));
     332                 :         15 :   filter->is_telecine = FALSE;
     333                 :         15 :   filter->first_buffer = TRUE;
     334                 :         15 :   filter->width = 0;
     335                 :         15 :   g_free (filter->comb_mask);
     336                 :         15 :   filter->comb_mask = NULL;
     337                 :         15 :   g_free (filter->block_scores);
     338                 :         15 :   filter->block_scores = NULL;
     339                 :         15 : }
     340                 :            : 
     341                 :            : static void
     342                 :          4 : gst_field_analysis_init (GstFieldAnalysis * filter,
     343                 :            :     GstFieldAnalysisClass * gclass)
     344                 :            : {
     345                 :          4 :   filter->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink");
     346                 :          4 :   gst_pad_set_setcaps_function (filter->sinkpad,
     347                 :          4 :       GST_DEBUG_FUNCPTR (gst_field_analysis_set_caps));
     348                 :          4 :   gst_pad_set_getcaps_function (filter->sinkpad,
     349                 :          4 :       GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps));
     350                 :          4 :   gst_pad_set_event_function (filter->sinkpad,
     351                 :          4 :       GST_DEBUG_FUNCPTR (gst_field_analysis_sink_event));
     352                 :          4 :   gst_pad_set_chain_function (filter->sinkpad,
     353                 :          4 :       GST_DEBUG_FUNCPTR (gst_field_analysis_chain));
     354                 :            : 
     355                 :          4 :   filter->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
     356                 :          4 :   gst_pad_set_getcaps_function (filter->srcpad,
     357                 :          4 :       GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps));
     358                 :            : 
     359                 :          4 :   gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad);
     360                 :          4 :   gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);
     361                 :            : 
     362                 :          4 :   filter->frames = g_queue_new ();
     363                 :          4 :   gst_field_analysis_reset (filter);
     364                 :          4 :   filter->same_field = &same_parity_ssd;
     365                 :          4 :   filter->field_thresh = DEFAULT_FIELD_THRESH;
     366                 :          4 :   filter->same_frame = &opposite_parity_5_tap;
     367                 :          4 :   filter->frame_thresh = DEFAULT_FRAME_THRESH;
     368                 :          4 :   filter->noise_floor = DEFAULT_NOISE_FLOOR;
     369                 :          4 :   filter->block_score_for_row = &block_score_for_row_5_tap;
     370                 :          4 :   filter->spatial_thresh = DEFAULT_SPATIAL_THRESH;
     371                 :          4 :   filter->block_width = DEFAULT_BLOCK_WIDTH;
     372                 :          4 :   filter->block_height = DEFAULT_BLOCK_HEIGHT;
     373                 :          4 :   filter->block_thresh = DEFAULT_BLOCK_THRESH;
     374                 :          4 :   filter->ignored_lines = DEFAULT_IGNORED_LINES;
     375                 :          4 : }
     376                 :            : 
     377                 :            : static void
     378                 :          0 : gst_field_analysis_set_property (GObject * object, guint prop_id,
     379                 :            :     const GValue * value, GParamSpec * pspec)
     380                 :            : {
     381                 :          0 :   GstFieldAnalysis *filter = GST_FIELDANALYSIS (object);
     382                 :            : 
     383   [ #  #  #  #  :          0 :   switch (prop_id) {
          #  #  #  #  #  
                #  #  # ]
     384                 :            :     case PROP_FIELD_METRIC:
     385   [ #  #  #  # ]:          0 :       switch (g_value_get_enum (value)) {
     386                 :            :         case GST_FIELDANALYSIS_SAD:
     387                 :          0 :           filter->same_field = &same_parity_sad;
     388                 :          0 :           break;
     389                 :            :         case GST_FIELDANALYSIS_SSD:
     390                 :          0 :           filter->same_field = &same_parity_ssd;
     391                 :          0 :           break;
     392                 :            :         case GST_FIELDANALYSIS_3_TAP:
     393                 :          0 :           filter->same_field = &same_parity_3_tap;
     394                 :          0 :           break;
     395                 :            :         default:
     396                 :          0 :           break;
     397                 :            :       }
     398                 :          0 :       break;
     399                 :            :     case PROP_FRAME_METRIC:
     400      [ #  #  # ]:          0 :       switch (g_value_get_enum (value)) {
     401                 :            :         case GST_FIELDANALYSIS_5_TAP:
     402                 :          0 :           filter->same_frame = &opposite_parity_5_tap;
     403                 :          0 :           break;
     404                 :            :         case GST_FIELDANALYSIS_WINDOWED_COMB:
     405                 :          0 :           filter->same_frame = &opposite_parity_windowed_comb;
     406                 :          0 :           break;
     407                 :            :         default:
     408                 :          0 :           break;
     409                 :            :       }
     410                 :          0 :       break;
     411                 :            :     case PROP_NOISE_FLOOR:
     412                 :          0 :       filter->noise_floor = g_value_get_uint (value);
     413                 :          0 :       break;
     414                 :            :     case PROP_FIELD_THRESH:
     415                 :          0 :       filter->field_thresh = g_value_get_float (value);
     416                 :          0 :       break;
     417                 :            :     case PROP_FRAME_THRESH:
     418                 :          0 :       filter->frame_thresh = g_value_get_float (value);
     419                 :          0 :       break;
     420                 :            :     case PROP_COMB_METHOD:
     421   [ #  #  #  # ]:          0 :       switch (g_value_get_enum (value)) {
     422                 :            :         case METHOD_32DETECT:
     423                 :          0 :           filter->block_score_for_row = &block_score_for_row_32detect;
     424                 :          0 :           break;
     425                 :            :         case METHOD_IS_COMBED:
     426                 :          0 :           filter->block_score_for_row = &block_score_for_row_iscombed;
     427                 :          0 :           break;
     428                 :            :         case METHOD_5_TAP:
     429                 :          0 :           filter->block_score_for_row = &block_score_for_row_5_tap;
     430                 :          0 :           break;
     431                 :            :         default:
     432                 :          0 :           break;
     433                 :            :       }
     434                 :          0 :       break;
     435                 :            :     case PROP_SPATIAL_THRESH:
     436                 :          0 :       filter->spatial_thresh = g_value_get_int64 (value);
     437                 :          0 :       break;
     438                 :            :     case PROP_BLOCK_WIDTH:
     439                 :          0 :       filter->block_width = g_value_get_uint64 (value);
     440         [ #  # ]:          0 :       if (filter->width) {
     441         [ #  # ]:          0 :         if (filter->block_scores) {
     442                 :          0 :           gsize nbytes = (filter->width / filter->block_width) * sizeof (guint);
     443                 :          0 :           filter->block_scores = g_realloc (filter->block_scores, nbytes);
     444                 :          0 :           memset (filter->block_scores, 0, nbytes);
     445                 :            :         } else {
     446                 :          0 :           filter->block_scores =
     447                 :          0 :               g_malloc0 ((filter->width / filter->block_width) *
     448                 :            :               sizeof (guint));
     449                 :            :         }
     450                 :            :       }
     451                 :          0 :       break;
     452                 :            :     case PROP_BLOCK_HEIGHT:
     453                 :          0 :       filter->block_height = g_value_get_uint64 (value);
     454                 :          0 :       break;
     455                 :            :     case PROP_BLOCK_THRESH:
     456                 :          0 :       filter->block_thresh = g_value_get_uint64 (value);
     457                 :          0 :       break;
     458                 :            :     case PROP_IGNORED_LINES:
     459                 :          0 :       filter->ignored_lines = g_value_get_uint64 (value);
     460                 :          0 :       break;
     461                 :            :     default:
     462                 :          0 :       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     463                 :          0 :       break;
     464                 :            :   }
     465                 :          0 : }
     466                 :            : 
     467                 :            : static void
     468                 :         11 : gst_field_analysis_get_property (GObject * object, guint prop_id,
     469                 :            :     GValue * value, GParamSpec * pspec)
     470                 :            : {
     471                 :         11 :   GstFieldAnalysis *filter = GST_FIELDANALYSIS (object);
     472                 :            : 
     473   [ +  +  +  +  :         11 :   switch (prop_id) {
          +  +  +  +  +  
                +  +  - ]
     474                 :            :     case PROP_FIELD_METRIC:
     475                 :            :     {
     476                 :          1 :       GstFieldAnalysisFieldMetric metric = DEFAULT_FIELD_METRIC;
     477         [ -  + ]:          1 :       if (filter->same_field == &same_parity_sad) {
     478                 :          0 :         metric = GST_FIELDANALYSIS_SAD;
     479         [ +  - ]:          1 :       } else if (filter->same_field == &same_parity_ssd) {
     480                 :          1 :         metric = GST_FIELDANALYSIS_SSD;
     481         [ #  # ]:          0 :       } else if (filter->same_field == &same_parity_3_tap) {
     482                 :          0 :         metric = GST_FIELDANALYSIS_3_TAP;
     483                 :            :       }
     484                 :          1 :       g_value_set_enum (value, metric);
     485                 :          1 :       break;
     486                 :            :     }
     487                 :            :     case PROP_FRAME_METRIC:
     488                 :            :     {
     489                 :          1 :       GstFieldAnalysisFrameMetric metric = DEFAULT_FRAME_METRIC;
     490         [ +  - ]:          1 :       if (filter->same_frame == &opposite_parity_5_tap) {
     491                 :          1 :         metric = GST_FIELDANALYSIS_5_TAP;
     492         [ #  # ]:          0 :       } else if (filter->same_frame == &opposite_parity_windowed_comb) {
     493                 :          0 :         metric = GST_FIELDANALYSIS_WINDOWED_COMB;
     494                 :            :       }
     495                 :          1 :       g_value_set_enum (value, metric);
     496                 :          1 :       break;
     497                 :            :     }
     498                 :            :     case PROP_NOISE_FLOOR:
     499                 :          1 :       g_value_set_uint (value, filter->noise_floor);
     500                 :          1 :       break;
     501                 :            :     case PROP_FIELD_THRESH:
     502                 :          1 :       g_value_set_float (value, filter->field_thresh);
     503                 :          1 :       break;
     504                 :            :     case PROP_FRAME_THRESH:
     505                 :          1 :       g_value_set_float (value, filter->frame_thresh);
     506                 :          1 :       break;
     507                 :            :     case PROP_COMB_METHOD:
     508                 :            :     {
     509                 :          1 :       FieldAnalysisCombMethod method = DEFAULT_COMB_METHOD;
     510         [ -  + ]:          1 :       if (filter->block_score_for_row == &block_score_for_row_32detect) {
     511                 :          0 :         method = METHOD_32DETECT;
     512         [ -  + ]:          1 :       } else if (filter->block_score_for_row == &block_score_for_row_iscombed) {
     513                 :          0 :         method = METHOD_IS_COMBED;
     514         [ +  - ]:          1 :       } else if (filter->block_score_for_row == &block_score_for_row_5_tap) {
     515                 :          1 :         method = METHOD_5_TAP;
     516                 :            :       }
     517                 :          1 :       g_value_set_enum (value, method);
     518                 :          1 :       break;
     519                 :            :     }
     520                 :            :     case PROP_SPATIAL_THRESH:
     521                 :          1 :       g_value_set_int64 (value, filter->spatial_thresh);
     522                 :          1 :       break;
     523                 :            :     case PROP_BLOCK_WIDTH:
     524                 :          1 :       g_value_set_uint64 (value, filter->block_width);
     525                 :          1 :       break;
     526                 :            :     case PROP_BLOCK_HEIGHT:
     527                 :          1 :       g_value_set_uint64 (value, filter->block_height);
     528                 :          1 :       break;
     529                 :            :     case PROP_BLOCK_THRESH:
     530                 :          1 :       g_value_set_uint64 (value, filter->block_thresh);
     531                 :          1 :       break;
     532                 :            :     case PROP_IGNORED_LINES:
     533                 :          1 :       g_value_set_uint64 (value, filter->ignored_lines);
     534                 :          1 :       break;
     535                 :            :     default:
     536                 :          0 :       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     537                 :          0 :       break;
     538                 :            :   }
     539                 :         11 : }
     540                 :            : 
     541                 :            : static void
     542                 :          0 : gst_field_analysis_update_format (GstFieldAnalysis * filter, GstCaps * caps)
     543                 :            : {
     544                 :            :   GstStructure *struc;
     545                 :            :   guint32 fourcc;
     546                 :            :   GstVideoFormat vformat;
     547                 :            :   gint width, height, data_offset, sample_incr, line_stride;
     548                 :            :   GQueue *outbufs;
     549                 :            : 
     550                 :          0 :   struc = gst_caps_get_structure (caps, 0);
     551                 :          0 :   gst_structure_get_fourcc (struc, "format", &fourcc);
     552                 :          0 :   vformat = gst_video_format_from_fourcc (fourcc);
     553                 :            : 
     554                 :          0 :   gst_structure_get_int (struc, "width", &width);
     555                 :          0 :   gst_structure_get_int (struc, "height", &height);
     556                 :            : 
     557                 :          0 :   data_offset =
     558                 :          0 :       gst_video_format_get_component_offset (vformat, 0, width, height);
     559                 :          0 :   sample_incr = gst_video_format_get_pixel_stride (vformat, 0);
     560                 :          0 :   line_stride = gst_video_format_get_row_stride (vformat, 0, width);
     561                 :            : 
     562                 :            :   /* if format is unchanged in our eyes, don't update the context */
     563 [ #  # ][ #  # ]:          0 :   if ((filter->width == width) && (filter->height == height)
     564         [ #  # ]:          0 :       && (filter->data_offset == data_offset)
     565         [ #  # ]:          0 :       && (filter->sample_incr == sample_incr)
     566         [ #  # ]:          0 :       && (filter->line_stride == line_stride))
     567                 :          0 :     return;
     568                 :            : 
     569                 :            :   /* format changed - process and push buffers before updating context */
     570                 :            : 
     571                 :          0 :   GST_OBJECT_LOCK (filter);
     572                 :          0 :   filter->flushing = TRUE;
     573                 :          0 :   outbufs = gst_field_analysis_flush_queue (filter, filter->frames);
     574                 :          0 :   GST_OBJECT_UNLOCK (filter);
     575                 :            : 
     576         [ #  # ]:          0 :   if (outbufs) {
     577         [ #  # ]:          0 :     while (g_queue_get_length (outbufs))
     578                 :          0 :       gst_pad_push (filter->srcpad, g_queue_pop_head (outbufs));
     579                 :            :   }
     580                 :            : 
     581                 :          0 :   GST_OBJECT_LOCK (filter);
     582                 :          0 :   filter->flushing = FALSE;
     583                 :            : 
     584                 :          0 :   filter->width = width;
     585                 :          0 :   filter->height = height;
     586                 :          0 :   filter->data_offset = data_offset;
     587                 :          0 :   filter->sample_incr = sample_incr;
     588                 :          0 :   filter->line_stride = line_stride;
     589                 :            : 
     590                 :            :   /* update allocations for metric scores */
     591         [ #  # ]:          0 :   if (filter->comb_mask) {
     592                 :          0 :     filter->comb_mask = g_realloc (filter->comb_mask, width);
     593                 :            :   } else {
     594                 :          0 :     filter->comb_mask = g_malloc (width);
     595                 :            :   }
     596         [ #  # ]:          0 :   if (filter->block_scores) {
     597                 :          0 :     gsize nbytes = (width / filter->block_width) * sizeof (guint);
     598                 :          0 :     filter->block_scores = g_realloc (filter->block_scores, nbytes);
     599                 :          0 :     memset (filter->block_scores, 0, nbytes);
     600                 :            :   } else {
     601                 :          0 :     filter->block_scores =
     602                 :          0 :         g_malloc0 ((width / filter->block_width) * sizeof (guint));
     603                 :            :   }
     604                 :            : 
     605                 :          0 :   GST_OBJECT_UNLOCK (filter);
     606                 :          0 :   return;
     607                 :            : }
     608                 :            : 
     609                 :            : static gboolean
     610                 :          0 : gst_field_analysis_set_caps (GstPad * pad, GstCaps * caps)
     611                 :            : {
     612                 :          0 :   gboolean ret = TRUE;
     613                 :          0 :   GstFieldAnalysis *filter = GST_FIELDANALYSIS (gst_pad_get_parent (pad));
     614                 :            : 
     615                 :          0 :   gst_field_analysis_update_format (filter, caps);
     616                 :            : 
     617                 :          0 :   ret = gst_pad_set_caps (filter->srcpad, caps);
     618                 :            : 
     619                 :          0 :   gst_object_unref (filter);
     620                 :            : 
     621                 :          0 :   return ret;
     622                 :            : }
     623                 :            : 
     624                 :            : #define FIELD_ANALYSIS_TOP_BOTTOM   (1 << 0)
     625                 :            : #define FIELD_ANALYSIS_BOTTOM_TOP   (1 << 1)
     626                 :            : #define FIELD_ANALYSIS_TOP_MATCH    (1 << 2)
     627                 :            : #define FIELD_ANALYSIS_BOTTOM_MATCH (1 << 3)
     628                 :            : 
     629                 :            : /* decorate removes a buffer from the internal queue, on which we have a ref,
     630                 :            :  * then makes its metadata writable (could be the same buffer, could be a new
     631                 :            :  * buffer, but either way we have a ref on it), decorates this buffer and
     632                 :            :  * returns it */
     633                 :            : static GstBuffer *
     634                 :          0 : gst_field_analysis_decorate (GstFieldAnalysis * filter, gboolean tff,
     635                 :            :     gboolean onefield, FieldAnalysisConclusion conclusion, gboolean gap)
     636                 :            : {
     637                 :          0 :   GstBuffer *buf = NULL;
     638                 :            :   GstCaps *caps;
     639                 :            : 
     640                 :          0 :   caps = gst_caps_copy (GST_PAD_CAPS (filter->srcpad));
     641                 :            : 
     642                 :            :   /* deal with incoming buffer */
     643 [ #  # ][ #  # ]:          0 :   if (conclusion > FIELD_ANALYSIS_PROGRESSIVE || filter->is_telecine == TRUE) {
     644                 :          0 :     gst_caps_set_simple (caps, "interlaced", G_TYPE_BOOLEAN, TRUE, NULL);
     645                 :          0 :     filter->is_telecine = conclusion != FIELD_ANALYSIS_INTERLACED;
     646         [ #  # ]:          0 :     if (conclusion >= FIELD_ANALYSIS_TELECINE_PROGRESSIVE
     647         [ #  # ]:          0 :         || filter->is_telecine == TRUE) {
     648                 :          0 :       gst_caps_set_simple (caps, "interlacing-method", G_TYPE_STRING,
     649                 :            :           "telecine", NULL);
     650                 :            :     } else {
     651                 :          0 :       gst_caps_set_simple (caps, "interlacing-method", G_TYPE_STRING, "unknown",
     652                 :            :           NULL);
     653                 :            :     }
     654                 :            :   } else {
     655                 :          0 :     gst_structure_remove_field (gst_caps_get_structure (caps, 0),
     656                 :            :         "interlacing-method");
     657                 :          0 :     gst_caps_set_simple (caps, "interlaced", G_TYPE_BOOLEAN, FALSE, NULL);
     658                 :            :   }
     659                 :            : 
     660                 :            :   /* get buffer from queue
     661                 :            :    * this takes our ref on the buf that was in the queue and gives us a buf
     662                 :            :    * on which we have a refi (could be the same buffer, but need not be) */
     663                 :          0 :   buf = gst_buffer_make_metadata_writable (g_queue_pop_head (filter->frames));
     664                 :            : 
     665                 :            :   /* set buffer flags */
     666         [ #  # ]:          0 :   if (!tff) {
     667                 :          0 :     GST_BUFFER_FLAG_UNSET (buf, GST_VIDEO_BUFFER_TFF);
     668 [ #  # ][ #  # ]:          0 :   } else if (tff == 1 || (tff == -1
     669         [ #  # ]:          0 :           && GST_BUFFER_FLAG_IS_SET (buf, GST_VIDEO_BUFFER_TFF))) {
     670                 :          0 :     GST_BUFFER_FLAG_SET (buf, GST_VIDEO_BUFFER_TFF);
     671                 :            :   }
     672                 :            : 
     673         [ #  # ]:          0 :   if (onefield) {
     674                 :          0 :     GST_BUFFER_FLAG_SET (buf, GST_VIDEO_BUFFER_ONEFIELD);
     675                 :            :   } else {
     676                 :          0 :     GST_BUFFER_FLAG_UNSET (buf, GST_VIDEO_BUFFER_ONEFIELD);
     677                 :            :   }
     678                 :            : 
     679                 :          0 :   GST_BUFFER_FLAG_UNSET (buf, GST_VIDEO_BUFFER_RFF);
     680                 :            : 
     681         [ #  # ]:          0 :   if (gap)
     682                 :          0 :     GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_GAP);
     683                 :            : 
     684 [ #  # ][ #  # ]:          0 :   if (conclusion == FIELD_ANALYSIS_TELECINE_PROGRESSIVE || (filter->is_telecine
     685         [ #  # ]:          0 :           && conclusion == FIELD_ANALYSIS_PROGRESSIVE))
     686                 :          0 :     GST_BUFFER_FLAG_SET (buf, GST_VIDEO_BUFFER_PROGRESSIVE);
     687                 :            : 
     688                 :            :   /* set the caps on the src pad and buffer before pushing */
     689         [ #  # ]:          0 :   if (gst_caps_is_equal (caps, GST_PAD_CAPS (filter->srcpad))) {
     690                 :          0 :     gst_buffer_set_caps (buf, GST_PAD_CAPS (filter->srcpad));
     691                 :            :   } else {
     692                 :          0 :     gboolean ret = TRUE;
     693                 :            : 
     694                 :          0 :     GST_OBJECT_UNLOCK (filter);
     695                 :          0 :     ret = gst_pad_set_caps (filter->srcpad, caps);
     696                 :          0 :     GST_OBJECT_LOCK (filter);
     697                 :            : 
     698         [ #  # ]:          0 :     if (!ret) {
     699         [ #  # ]:          0 :       GST_ERROR_OBJECT (filter, "Could not set pad caps");
     700                 :          0 :       gst_buffer_unref (buf);
     701                 :          0 :       return NULL;
     702                 :            :     }
     703                 :          0 :     gst_buffer_set_caps (buf, caps);
     704                 :            :   }
     705                 :            :   /* drop our ref to the caps as the buffer and pad have their own */
     706                 :          0 :   gst_caps_unref (caps);
     707                 :            : 
     708         [ #  # ]:          0 :   GST_DEBUG_OBJECT (filter,
     709                 :            :       "Pushing buffer with flags: %p (%p), p %d, tff %d, 1f %d, gap %d; conc %d",
     710                 :            :       GST_BUFFER_DATA (buf), buf,
     711                 :            :       GST_BUFFER_FLAG_IS_SET (buf, GST_VIDEO_BUFFER_PROGRESSIVE),
     712                 :            :       GST_BUFFER_FLAG_IS_SET (buf, GST_VIDEO_BUFFER_TFF),
     713                 :            :       GST_BUFFER_FLAG_IS_SET (buf, GST_VIDEO_BUFFER_ONEFIELD),
     714                 :            :       GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_GAP), conclusion);
     715                 :            : 
     716                 :          0 :   return buf;
     717                 :            : }
     718                 :            : 
     719                 :            : /* _flush_one does not touch the buffer ref counts directly but _decorate ()
     720                 :            :  * has some influence on ref counts - see its annotation for details */
     721                 :            : static GstBuffer *
     722                 :          0 : gst_field_analysis_flush_one (GstFieldAnalysis * filter, GQueue * outbufs)
     723                 :            : {
     724                 :          0 :   GstBuffer *buf = NULL;
     725                 :          0 :   guint n_queued = g_queue_get_length (filter->frames);
     726                 :          0 :   guint idx = n_queued - 1;
     727                 :            : 
     728 [ #  # ][ #  # ]:          0 :   if (!n_queued || n_queued > 2)
     729                 :          0 :     return buf;
     730                 :            : 
     731         [ #  # ]:          0 :   GST_DEBUG_OBJECT (filter, "Flushing last buffer (queue length %d)", n_queued);
     732         [ #  # ]:          0 :   if (filter->results[idx].holding == 1 + TOP_FIELD
     733         [ #  # ]:          0 :       || filter->results[idx].holding == 1 + BOTTOM_FIELD) {
     734                 :            :     /* should be only one field needed */
     735                 :          0 :     buf =
     736                 :          0 :         gst_field_analysis_decorate (filter,
     737                 :          0 :         filter->results[idx].holding == 1 + TOP_FIELD, TRUE,
     738                 :            :         filter->results[idx].conclusion, FALSE);
     739                 :            :   } else {
     740                 :            :     /* possibility that both fields are needed */
     741                 :          0 :     buf =
     742                 :          0 :         gst_field_analysis_decorate (filter, -1, FALSE,
     743                 :          0 :         filter->results[idx].conclusion, !filter->results[idx].holding);
     744                 :            :   }
     745         [ #  # ]:          0 :   if (buf) {
     746         [ #  # ]:          0 :     if (outbufs)
     747                 :          0 :       g_queue_push_tail (outbufs, buf);
     748                 :            :   } else {
     749         [ #  # ]:          0 :     GST_DEBUG_OBJECT (filter, "Error occurred during decoration");
     750                 :            :   }
     751                 :          0 :   return buf;
     752                 :            : }
     753                 :            : 
     754                 :            : /* _flush_queue () has no direct influence on refcounts and nor does _flush_one,
     755                 :            :  * but _decorate () does and so this function does indirectly */
     756                 :            : static GQueue *
     757                 :          0 : gst_field_analysis_flush_queue (GstFieldAnalysis * filter, GQueue * queue)
     758                 :            : {
     759                 :            :   GQueue *outbufs;
     760                 :          0 :   guint length = 0;
     761                 :            : 
     762         [ #  # ]:          0 :   if (queue)
     763                 :          0 :     length = g_queue_get_length (queue);
     764                 :            : 
     765         [ #  # ]:          0 :   if (length < 2)
     766                 :          0 :     return NULL;
     767                 :            : 
     768                 :          0 :   outbufs = g_queue_new ();
     769                 :            : 
     770         [ #  # ]:          0 :   while (length) {
     771                 :          0 :     gst_field_analysis_flush_one (filter, outbufs);
     772                 :          0 :     length--;
     773                 :            :   }
     774                 :            : 
     775                 :          0 :   return outbufs;
     776                 :            : }
     777                 :            : 
     778                 :            : static gboolean
     779                 :          0 : gst_field_analysis_sink_event (GstPad * pad, GstEvent * event)
     780                 :            : {
     781                 :          0 :   GstFieldAnalysis *filter = GST_FIELDANALYSIS (gst_pad_get_parent (pad));
     782                 :            : 
     783         [ #  # ]:          0 :   GST_LOG_OBJECT (pad, "received %s event: %" GST_PTR_FORMAT,
     784                 :            :       GST_EVENT_TYPE_NAME (event), event);
     785                 :            : 
     786      [ #  #  # ]:          0 :   switch (GST_EVENT_TYPE (event)) {
     787                 :            :     case GST_EVENT_NEWSEGMENT:
     788                 :            :     case GST_EVENT_EOS:
     789                 :            :     {
     790                 :            :       /* for both NEWSEGMENT and EOS it is safest to process and push queued
     791                 :            :        * buffers */
     792                 :            :       GQueue *outbufs;
     793                 :            : 
     794                 :          0 :       GST_OBJECT_LOCK (filter);
     795                 :          0 :       filter->flushing = TRUE;
     796                 :          0 :       outbufs = gst_field_analysis_flush_queue (filter, filter->frames);
     797                 :          0 :       GST_OBJECT_UNLOCK (filter);
     798                 :            : 
     799         [ #  # ]:          0 :       if (outbufs) {
     800         [ #  # ]:          0 :         while (g_queue_get_length (outbufs))
     801                 :          0 :           gst_pad_push (filter->srcpad, g_queue_pop_head (outbufs));
     802                 :            :       }
     803                 :            : 
     804                 :          0 :       GST_OBJECT_LOCK (filter);
     805                 :          0 :       filter->flushing = FALSE;
     806                 :          0 :       GST_OBJECT_UNLOCK (filter);
     807                 :          0 :       break;
     808                 :            :     }
     809                 :            :     case GST_EVENT_FLUSH_STOP:
     810                 :            :       /* if we have any buffers left in the queue, unref them until the queue
     811                 :            :        * is empty */
     812                 :          0 :       GST_OBJECT_LOCK (filter);
     813                 :          0 :       gst_field_analysis_reset (filter);
     814                 :          0 :       GST_OBJECT_UNLOCK (filter);
     815                 :          0 :       break;
     816                 :            :     default:
     817                 :          0 :       break;
     818                 :            :   }
     819                 :            : 
     820                 :            :   /* NOTE: we always forward the event currently, change this as necessary */
     821                 :          0 :   return gst_pad_push_event (filter->srcpad, event);
     822                 :            : }
     823                 :            : 
     824                 :            : 
     825                 :            : static gfloat
     826                 :          0 : same_parity_sad (GstFieldAnalysis * filter, FieldAnalysisFields * fields)
     827                 :            : {
     828                 :            :   gint j;
     829                 :            :   gfloat sum;
     830                 :            :   guint8 *f1j, *f2j;
     831                 :            : 
     832                 :          0 :   const gint y_offset = filter->data_offset;
     833                 :          0 :   const gint stride = filter->line_stride;
     834                 :          0 :   const gint stridex2 = stride << 1;
     835                 :          0 :   const guint32 noise_floor = filter->noise_floor;
     836                 :            : 
     837                 :          0 :   f1j = GST_BUFFER_DATA (fields[0].buf) + y_offset + fields[0].parity * stride;
     838                 :          0 :   f2j = GST_BUFFER_DATA (fields[1].buf) + y_offset + fields[1].parity * stride;
     839                 :            : 
     840                 :          0 :   sum = 0.0f;
     841         [ #  # ]:          0 :   for (j = 0; j < (filter->height >> 1); j++) {
     842                 :          0 :     guint32 tempsum = 0;
     843                 :          0 :     orc_same_parity_sad_planar_yuv (&tempsum, f1j, f2j, noise_floor,
     844                 :            :         filter->width);
     845                 :          0 :     sum += tempsum;
     846                 :          0 :     f1j += stridex2;
     847                 :          0 :     f2j += stridex2;
     848                 :            :   }
     849                 :            : 
     850                 :          0 :   return sum / (0.5f * filter->width * filter->height);
     851                 :            : }
     852                 :            : 
     853                 :            : static gfloat
     854                 :          0 : same_parity_ssd (GstFieldAnalysis * filter, FieldAnalysisFields * fields)
     855                 :            : {
     856                 :            :   gint j;
     857                 :            :   gfloat sum;
     858                 :            :   guint8 *f1j, *f2j;
     859                 :            : 
     860                 :          0 :   const gint y_offset = filter->data_offset;
     861                 :          0 :   const gint stride = filter->line_stride;
     862                 :          0 :   const gint stridex2 = stride << 1;
     863                 :            :   /* noise floor needs to be squared for SSD */
     864                 :          0 :   const guint32 noise_floor = filter->noise_floor * filter->noise_floor;
     865                 :            : 
     866                 :          0 :   f1j = GST_BUFFER_DATA (fields[0].buf) + y_offset + fields[0].parity * stride;
     867                 :          0 :   f2j = GST_BUFFER_DATA (fields[1].buf) + y_offset + fields[1].parity * stride;
     868                 :            : 
     869                 :          0 :   sum = 0.0f;
     870         [ #  # ]:          0 :   for (j = 0; j < (filter->height >> 1); j++) {
     871                 :          0 :     guint32 tempsum = 0;
     872                 :          0 :     orc_same_parity_ssd_planar_yuv (&tempsum, f1j, f2j, noise_floor,
     873                 :            :         filter->width);
     874                 :          0 :     sum += tempsum;
     875                 :          0 :     f1j += stridex2;
     876                 :          0 :     f2j += stridex2;
     877                 :            :   }
     878                 :            : 
     879                 :          0 :   return sum / (0.5f * filter->width * filter->height); /* field is half height */
     880                 :            : }
     881                 :            : 
     882                 :            : /* horizontal [1,4,1] diff between fields - is this a good idea or should the
     883                 :            :  * current sample be emphasised more or less? */
     884                 :            : static gfloat
     885                 :          0 : same_parity_3_tap (GstFieldAnalysis * filter, FieldAnalysisFields * fields)
     886                 :            : {
     887                 :            :   gint i, j;
     888                 :            :   gfloat sum;
     889                 :            :   guint8 *f1j, *f2j;
     890                 :            : 
     891                 :          0 :   const gint y_offset = filter->data_offset;
     892                 :          0 :   const gint stride = filter->line_stride;
     893                 :          0 :   const gint stridex2 = stride << 1;
     894                 :          0 :   const gint incr = filter->sample_incr;
     895                 :            :   /* noise floor needs to be squared for [1,4,1] */
     896                 :          0 :   const guint32 noise_floor = filter->noise_floor * 6;
     897                 :            : 
     898                 :          0 :   f1j = GST_BUFFER_DATA (fields[0].buf) + y_offset + fields[0].parity * stride;
     899                 :          0 :   f2j = GST_BUFFER_DATA (fields[1].buf) + y_offset + fields[1].parity * stride;
     900                 :            : 
     901                 :          0 :   sum = 0.0f;
     902         [ #  # ]:          0 :   for (j = 0; j < (filter->height >> 1); j++) {
     903                 :          0 :     guint32 tempsum = 0;
     904                 :            :     guint32 diff;
     905                 :            : 
     906                 :            :     /* unroll first as it is a special case */
     907                 :          0 :     diff = abs (((f1j[0] << 2) + (f1j[incr] << 1))
     908                 :          0 :         - ((f2j[0] << 2) + (f2j[incr] << 1)));
     909         [ #  # ]:          0 :     if (diff > noise_floor)
     910                 :          0 :       sum += diff;
     911                 :            : 
     912                 :          0 :     orc_same_parity_3_tap_planar_yuv (&tempsum, f1j, &f1j[incr],
     913                 :          0 :         &f1j[incr << 1], f2j, &f2j[incr], &f2j[incr << 1], noise_floor,
     914                 :          0 :         filter->width - 1);
     915                 :          0 :     sum += tempsum;
     916                 :            : 
     917                 :            :     /* unroll last as it is a special case */
     918                 :          0 :     i = filter->width - 1;
     919                 :          0 :     diff = abs (((f1j[i - incr] << 1) + (f1j[i] << 2))
     920                 :          0 :         - ((f2j[i - incr] << 1) + (f2j[i] << 2)));
     921         [ #  # ]:          0 :     if (diff > noise_floor)
     922                 :          0 :       sum += diff;
     923                 :            : 
     924                 :          0 :     f1j += stridex2;
     925                 :          0 :     f2j += stridex2;
     926                 :            :   }
     927                 :            : 
     928                 :          0 :   return sum / ((6.0f / 2.0f) * filter->width * filter->height);        /* 1 + 4 + 1 = 6; field is half height */
     929                 :            : }
     930                 :            : 
     931                 :            : /* vertical [1,-3,4,-3,1] - same as is used in FieldDiff from TIVTC,
     932                 :            :  * tritical's AVISynth IVTC filter */
     933                 :            : /* 0th field's parity defines operation */
     934                 :            : static gfloat
     935                 :          0 : opposite_parity_5_tap (GstFieldAnalysis * filter, FieldAnalysisFields * fields)
     936                 :            : {
     937                 :            :   gint j;
     938                 :            :   gfloat sum;
     939                 :            :   guint8 *fjm2, *fjm1, *fj, *fjp1, *fjp2;
     940                 :            :   guint32 tempsum;
     941                 :            : 
     942                 :          0 :   const gint y_offset = filter->data_offset;
     943                 :          0 :   const gint stride = filter->line_stride;
     944                 :          0 :   const gint stridex2 = stride << 1;
     945                 :            :   /* noise floor needs to be *6 for [1,-3,4,-3,1] */
     946                 :          0 :   const guint32 noise_floor = filter->noise_floor * 6;
     947                 :            : 
     948                 :          0 :   sum = 0.0f;
     949                 :            : 
     950                 :            :   /* fj is line j of the combined frame made from the top field even lines of
     951                 :            :    *   field 0 and the bottom field odd lines from field 1
     952                 :            :    * fjp1 is one line down from fj
     953                 :            :    * fjm2 is two lines up from fj
     954                 :            :    * fj with j == 0 is the 0th line of the top field
     955                 :            :    * fj with j == 1 is the 0th line of the bottom field or the 1st field of
     956                 :            :    *   the frame*/
     957                 :            : 
     958                 :            :   /* unroll first line as it is a special case */
     959         [ #  # ]:          0 :   if (fields[0].parity == TOP_FIELD) {
     960                 :          0 :     fj = GST_BUFFER_DATA (fields[0].buf) + y_offset;
     961                 :          0 :     fjp1 = GST_BUFFER_DATA (fields[1].buf) + y_offset + stride;
     962                 :            :   } else {
     963                 :          0 :     fj = GST_BUFFER_DATA (fields[1].buf) + y_offset;
     964                 :          0 :     fjp1 = GST_BUFFER_DATA (fields[0].buf) + y_offset + stride;
     965                 :            :   }
     966                 :          0 :   fjp2 = fj + stridex2;
     967                 :            : 
     968                 :          0 :   tempsum = 0;
     969                 :          0 :   orc_opposite_parity_5_tap_planar_yuv (&tempsum, fjp2, fjp1, fj, fjp1, fjp2,
     970                 :            :       noise_floor, filter->width);
     971                 :          0 :   sum += tempsum;
     972                 :            : 
     973         [ #  # ]:          0 :   for (j = 1; j < (filter->height >> 1) - 1; j++) {
     974                 :            :     /* shift everything down a line in the field of interest (means += stridex2) */
     975                 :          0 :     fjm2 = fj;
     976                 :          0 :     fjm1 = fjp1;
     977                 :          0 :     fj = fjp2;
     978                 :          0 :     fjp1 += stridex2;
     979                 :          0 :     fjp2 += stridex2;
     980                 :            : 
     981                 :          0 :     tempsum = 0;
     982                 :          0 :     orc_opposite_parity_5_tap_planar_yuv (&tempsum, fjm2, fjm1, fj, fjp1, fjp2,
     983                 :            :         noise_floor, filter->width);
     984                 :          0 :     sum += tempsum;
     985                 :            :   }
     986                 :            : 
     987                 :            :   /* unroll the last line as it is a special case */
     988                 :            :   /* shift everything down a line in the field of interest (means += stridex2) */
     989                 :          0 :   fjm2 = fj;
     990                 :          0 :   fjm1 = fjp1;
     991                 :          0 :   fj = fjp2;
     992                 :            : 
     993                 :          0 :   tempsum = 0;
     994                 :          0 :   orc_opposite_parity_5_tap_planar_yuv (&tempsum, fjm2, fjm1, fj, fjm1, fjm2,
     995                 :            :       noise_floor, filter->width);
     996                 :          0 :   sum += tempsum;
     997                 :            : 
     998                 :          0 :   return sum / ((6.0f / 2.0f) * filter->width * filter->height);        /* 1 + 4 + 1 == 3 + 3 == 6; field is half height */
     999                 :            : }
    1000                 :            : 
    1001                 :            : /* this metric was sourced from HandBrake but originally from transcode
    1002                 :            :  * the return value is the highest block score for the row of blocks */
    1003                 :            : static inline guint64
    1004                 :          0 : block_score_for_row_32detect (GstFieldAnalysis * filter, guint8 * base_fj,
    1005                 :            :     guint8 * base_fjp1)
    1006                 :            : {
    1007                 :            :   guint64 i, j;
    1008                 :          0 :   guint8 *comb_mask = filter->comb_mask;
    1009                 :          0 :   guint *block_scores = filter->block_scores;
    1010                 :            :   guint64 block_score;
    1011                 :            :   guint8 *fjm2, *fjm1, *fj, *fjp1;
    1012                 :          0 :   const gint incr = filter->sample_incr;
    1013                 :          0 :   const gint stridex2 = filter->line_stride << 1;
    1014                 :          0 :   const guint64 block_width = filter->block_width;
    1015                 :          0 :   const guint64 block_height = filter->block_height;
    1016                 :          0 :   const gint64 spatial_thresh = filter->spatial_thresh;
    1017                 :          0 :   const gint width = filter->width - (filter->width % block_width);
    1018                 :            : 
    1019                 :          0 :   fjm2 = base_fj - stridex2;
    1020                 :          0 :   fjm1 = base_fjp1 - stridex2;
    1021                 :          0 :   fj = base_fj;
    1022                 :          0 :   fjp1 = base_fjp1;
    1023                 :            : 
    1024         [ #  # ]:          0 :   for (j = 0; j < block_height; j++) {
    1025                 :            :     /* we have to work one result ahead of ourselves which results in some small
    1026                 :            :      * peculiarities below */
    1027                 :            :     gint diff1, diff2;
    1028                 :            : 
    1029                 :          0 :     diff1 = fj[0] - fjm1[0];
    1030                 :          0 :     diff2 = fj[0] - fjp1[0];
    1031                 :            :     /* change in the same direction */
    1032 [ #  # ][ #  # ]:          0 :     if ((diff1 > spatial_thresh && diff2 > spatial_thresh)
    1033 [ #  # ][ #  # ]:          0 :         || (diff1 < -spatial_thresh && diff2 < -spatial_thresh)) {
    1034 [ #  # ][ #  # ]:          0 :       comb_mask[0] = abs (fj[0] - fjm2[0]) < 10 && abs (fj[0] - fjm1[0]) > 15;
                 [ #  # ]
    1035                 :            :     } else {
    1036                 :          0 :       comb_mask[0] = FALSE;
    1037                 :            :     }
    1038                 :            : 
    1039         [ #  # ]:          0 :     for (i = 1; i < width; i++) {
    1040                 :          0 :       const guint64 idx = i * incr;
    1041                 :          0 :       const guint64 res_idx = (i - 1) / block_width;
    1042                 :            : 
    1043                 :          0 :       diff1 = fj[idx] - fjm1[idx];
    1044                 :          0 :       diff2 = fj[idx] - fjp1[idx];
    1045 [ #  # ][ #  # ]:          0 :       if ((diff1 > spatial_thresh && diff2 > spatial_thresh)
    1046 [ #  # ][ #  # ]:          0 :           || (diff1 < -spatial_thresh && diff2 < -spatial_thresh)) {
    1047 [ #  # ][ #  # ]:          0 :         comb_mask[i] = abs (fj[idx] - fjm2[idx]) < 10
    1048         [ #  # ]:          0 :             && abs (fj[idx] - fjm1[idx]) > 15;
    1049                 :            :       } else {
    1050                 :          0 :         comb_mask[i] = FALSE;
    1051                 :            :       }
    1052                 :            : 
    1053 [ #  # ][ #  # ]:          0 :       if (i == 1 && comb_mask[i - 1] && comb_mask[i]) {
                 [ #  # ]
    1054                 :            :         /* left edge */
    1055                 :          0 :         block_scores[res_idx]++;
    1056         [ #  # ]:          0 :       } else if (i == width - 1) {
    1057                 :            :         /* right edge */
    1058 [ #  # ][ #  # ]:          0 :         if (comb_mask[i - 2] && comb_mask[i - 1] && comb_mask[i])
                 [ #  # ]
    1059                 :          0 :           block_scores[res_idx]++;
    1060 [ #  # ][ #  # ]:          0 :         if (comb_mask[i - 1] && comb_mask[i])
    1061                 :          0 :           block_scores[i / block_width]++;
    1062 [ #  # ][ #  # ]:          0 :       } else if (comb_mask[i - 2] && comb_mask[i - 1] && comb_mask[i]) {
                 [ #  # ]
    1063                 :          0 :         block_scores[res_idx]++;
    1064                 :            :       }
    1065                 :            :     }
    1066                 :            :     /* advance down a line */
    1067                 :          0 :     fjm2 = fjm1;
    1068                 :          0 :     fjm1 = fj;
    1069                 :          0 :     fj = fjp1;
    1070                 :          0 :     fjp1 = fjm1 + stridex2;
    1071                 :            :   }
    1072                 :            : 
    1073                 :          0 :   block_score = 0;
    1074         [ #  # ]:          0 :   for (i = 0; i < width / block_width; i++) {
    1075         [ #  # ]:          0 :     if (block_scores[i] > block_score)
    1076                 :          0 :       block_score = block_scores[i];
    1077                 :            :   }
    1078                 :            : 
    1079                 :          0 :   g_free (block_scores);
    1080                 :          0 :   g_free (comb_mask);
    1081                 :          0 :   return block_score;
    1082                 :            : }
    1083                 :            : 
    1084                 :            : /* this metric was sourced from HandBrake but originally from
    1085                 :            :  * tritical's isCombedT Avisynth function
    1086                 :            :  * the return value is the highest block score for the row of blocks */
    1087                 :            : static inline guint64
    1088                 :          0 : block_score_for_row_iscombed (GstFieldAnalysis * filter, guint8 * base_fj,
    1089                 :            :     guint8 * base_fjp1)
    1090                 :            : {
    1091                 :            :   guint64 i, j;
    1092                 :          0 :   guint8 *comb_mask = filter->comb_mask;
    1093                 :          0 :   guint *block_scores = filter->block_scores;
    1094                 :            :   guint64 block_score;
    1095                 :            :   guint8 *fjm1, *fj, *fjp1;
    1096                 :          0 :   const gint incr = filter->sample_incr;
    1097                 :          0 :   const gint stridex2 = filter->line_stride << 1;
    1098                 :          0 :   const guint64 block_width = filter->block_width;
    1099                 :          0 :   const guint64 block_height = filter->block_height;
    1100                 :          0 :   const gint64 spatial_thresh = filter->spatial_thresh;
    1101                 :          0 :   const gint64 spatial_thresh_squared = spatial_thresh * spatial_thresh;
    1102                 :          0 :   const gint width = filter->width - (filter->width % block_width);
    1103                 :            : 
    1104                 :          0 :   fjm1 = base_fjp1 - stridex2;
    1105                 :          0 :   fj = base_fj;
    1106                 :          0 :   fjp1 = base_fjp1;
    1107                 :            : 
    1108         [ #  # ]:          0 :   for (j = 0; j < block_height; j++) {
    1109                 :            :     /* we have to work one result ahead of ourselves which results in some small
    1110                 :            :      * peculiarities below */
    1111                 :            :     gint diff1, diff2;
    1112                 :            : 
    1113                 :          0 :     diff1 = fj[0] - fjm1[0];
    1114                 :          0 :     diff2 = fj[0] - fjp1[0];
    1115                 :            :     /* change in the same direction */
    1116 [ #  # ][ #  # ]:          0 :     if ((diff1 > spatial_thresh && diff2 > spatial_thresh)
    1117 [ #  # ][ #  # ]:          0 :         || (diff1 < -spatial_thresh && diff2 < -spatial_thresh)) {
    1118                 :          0 :       comb_mask[0] =
    1119                 :          0 :           (fjm1[0] - fj[0]) * (fjp1[0] - fj[0]) > spatial_thresh_squared;
    1120                 :            :     } else {
    1121                 :          0 :       comb_mask[0] = FALSE;
    1122                 :            :     }
    1123                 :            : 
    1124         [ #  # ]:          0 :     for (i = 1; i < width; i++) {
    1125                 :          0 :       const guint64 idx = i * incr;
    1126                 :          0 :       const guint64 res_idx = (i - 1) / block_width;
    1127                 :            : 
    1128                 :          0 :       diff1 = fj[idx] - fjm1[idx];
    1129                 :          0 :       diff2 = fj[idx] - fjp1[idx];
    1130 [ #  # ][ #  # ]:          0 :       if ((diff1 > spatial_thresh && diff2 > spatial_thresh)
    1131 [ #  # ][ #  # ]:          0 :           || (diff1 < -spatial_thresh && diff2 < -spatial_thresh)) {
    1132                 :          0 :         comb_mask[i] =
    1133                 :          0 :             (fjm1[idx] - fj[idx]) * (fjp1[idx] - fj[idx]) >
    1134                 :            :             spatial_thresh_squared;
    1135                 :            :       } else {
    1136                 :          0 :         comb_mask[i] = FALSE;
    1137                 :            :       }
    1138                 :            : 
    1139 [ #  # ][ #  # ]:          0 :       if (i == 1 && comb_mask[i - 1] && comb_mask[i]) {
                 [ #  # ]
    1140                 :            :         /* left edge */
    1141                 :          0 :         block_scores[res_idx]++;
    1142         [ #  # ]:          0 :       } else if (i == width - 1) {
    1143                 :            :         /* right edge */
    1144 [ #  # ][ #  # ]:          0 :         if (comb_mask[i - 2] && comb_mask[i - 1] && comb_mask[i])
                 [ #  # ]
    1145                 :          0 :           block_scores[res_idx]++;
    1146 [ #  # ][ #  # ]:          0 :         if (comb_mask[i - 1] && comb_mask[i])
    1147                 :          0 :           block_scores[i / block_width]++;
    1148 [ #  # ][ #  # ]:          0 :       } else if (comb_mask[i - 2] && comb_mask[i - 1] && comb_mask[i]) {
                 [ #  # ]
    1149                 :          0 :         block_scores[res_idx]++;
    1150                 :            :       }
    1151                 :            :     }
    1152                 :            :     /* advance down a line */
    1153                 :          0 :     fjm1 = fj;
    1154                 :          0 :     fj = fjp1;
    1155                 :          0 :     fjp1 = fjm1 + stridex2;
    1156                 :            :   }
    1157                 :            : 
    1158                 :          0 :   block_score = 0;
    1159         [ #  # ]:          0 :   for (i = 0; i < width / block_width; i++) {
    1160         [ #  # ]:          0 :     if (block_scores[i] > block_score)
    1161                 :          0 :       block_score = block_scores[i];
    1162                 :            :   }
    1163                 :            : 
    1164                 :          0 :   g_free (block_scores);
    1165                 :          0 :   g_free (comb_mask);
    1166                 :          0 :   return block_score;
    1167                 :            : }
    1168                 :            : 
    1169                 :            : /* this metric was sourced from HandBrake but originally from
    1170                 :            :  * tritical's isCombedT Avisynth function
    1171                 :            :  * the return value is the highest block score for the row of blocks */
    1172                 :            : static inline guint64
    1173                 :          0 : block_score_for_row_5_tap (GstFieldAnalysis * filter, guint8 * base_fj,
    1174                 :            :     guint8 * base_fjp1)
    1175                 :            : {
    1176                 :            :   guint64 i, j;
    1177                 :          0 :   guint8 *comb_mask = filter->comb_mask;
    1178                 :          0 :   guint *block_scores = filter->block_scores;
    1179                 :            :   guint64 block_score;
    1180                 :            :   guint8 *fjm2, *fjm1, *fj, *fjp1, *fjp2;
    1181                 :          0 :   const gint incr = filter->sample_incr;
    1182                 :          0 :   const gint stridex2 = filter->line_stride << 1;
    1183                 :          0 :   const guint64 block_width = filter->block_width;
    1184                 :          0 :   const guint64 block_height = filter->block_height;
    1185                 :          0 :   const gint64 spatial_thresh = filter->spatial_thresh;
    1186                 :          0 :   const gint64 spatial_threshx6 = 6 * spatial_thresh;
    1187                 :          0 :   const gint width = filter->width - (filter->width % block_width);
    1188                 :            : 
    1189                 :          0 :   fjm2 = base_fj - stridex2;
    1190                 :          0 :   fjm1 = base_fjp1 - stridex2;
    1191                 :          0 :   fj = base_fj;
    1192                 :          0 :   fjp1 = base_fjp1;
    1193                 :          0 :   fjp2 = fj + stridex2;
    1194                 :            : 
    1195         [ #  # ]:          0 :   for (j = 0; j < block_height; j++) {
    1196                 :            :     /* we have to work one result ahead of ourselves which results in some small
    1197                 :            :      * peculiarities below */
    1198                 :            :     gint diff1, diff2;
    1199                 :            : 
    1200                 :          0 :     diff1 = fj[0] - fjm1[0];
    1201                 :          0 :     diff2 = fj[0] - fjp1[0];
    1202                 :            :     /* change in the same direction */
    1203 [ #  # ][ #  # ]:          0 :     if ((diff1 > spatial_thresh && diff2 > spatial_thresh)
    1204 [ #  # ][ #  # ]:          0 :         || (diff1 < -spatial_thresh && diff2 < -spatial_thresh)) {
    1205                 :          0 :       comb_mask[0] =
    1206                 :          0 :           abs (fjm2[0] + (fj[0] << 2) + fjp2[0] - 3 * (fjm1[0] + fjp1[0])) >
    1207                 :            :           spatial_threshx6;
    1208                 :            : 
    1209                 :            :       /* motion detection that needs previous and next frames
    1210                 :            :          this isn't really necessary, but acts as an optimisation if the
    1211                 :            :          additional delay isn't a problem
    1212                 :            :          if (motion_detection) {
    1213                 :            :          if (abs(fpj[idx] - fj[idx]               ) > motion_thresh &&
    1214                 :            :          abs(           fjm1[idx] - fnjm1[idx]) > motion_thresh &&
    1215                 :            :          abs(           fjp1[idx] - fnjp1[idx]) > motion_thresh)
    1216                 :            :          motion++;
    1217                 :            :          if (abs(             fj[idx]   - fnj[idx]) > motion_thresh &&
    1218                 :            :          abs(fpjm1[idx] - fjm1[idx]           ) > motion_thresh &&
    1219                 :            :          abs(fpjp1[idx] - fjp1[idx]           ) > motion_thresh)
    1220                 :            :          motion++;
    1221                 :            :          } else {
    1222                 :            :          motion = 1;
    1223                 :            :          }
    1224                 :            :        */
    1225                 :            :     } else {
    1226                 :          0 :       comb_mask[0] = FALSE;
    1227                 :            :     }
    1228                 :            : 
    1229         [ #  # ]:          0 :     for (i = 1; i < width; i++) {
    1230                 :          0 :       const guint64 idx = i * incr;
    1231                 :          0 :       const guint64 res_idx = (i - 1) / block_width;
    1232                 :            : 
    1233                 :          0 :       diff1 = fj[idx] - fjm1[idx];
    1234                 :          0 :       diff2 = fj[idx] - fjp1[idx];
    1235 [ #  # ][ #  # ]:          0 :       if ((diff1 > spatial_thresh && diff2 > spatial_thresh)
    1236 [ #  # ][ #  # ]:          0 :           || (diff1 < -spatial_thresh && diff2 < -spatial_thresh)) {
    1237                 :          0 :         comb_mask[i] =
    1238                 :          0 :             abs (fjm2[idx] + (fj[idx] << 2) + fjp2[idx] - 3 * (fjm1[idx] +
    1239                 :          0 :                 fjp1[idx])) > spatial_threshx6;
    1240                 :            :       } else {
    1241                 :          0 :         comb_mask[i] = FALSE;
    1242                 :            :       }
    1243                 :            : 
    1244 [ #  # ][ #  # ]:          0 :       if (i == 1 && comb_mask[i - 1] && comb_mask[i]) {
                 [ #  # ]
    1245                 :            :         /* left edge */
    1246                 :          0 :         block_scores[res_idx]++;
    1247         [ #  # ]:          0 :       } else if (i == width - 1) {
    1248                 :            :         /* right edge */
    1249 [ #  # ][ #  # ]:          0 :         if (comb_mask[i - 2] && comb_mask[i - 1] && comb_mask[i])
                 [ #  # ]
    1250                 :          0 :           block_scores[res_idx]++;
    1251 [ #  # ][ #  # ]:          0 :         if (comb_mask[i - 1] && comb_mask[i])
    1252                 :          0 :           block_scores[i / block_width]++;
    1253 [ #  # ][ #  # ]:          0 :       } else if (comb_mask[i - 2] && comb_mask[i - 1] && comb_mask[i]) {
                 [ #  # ]
    1254                 :          0 :         block_scores[res_idx]++;
    1255                 :            :       }
    1256                 :            :     }
    1257                 :            :     /* advance down a line */
    1258                 :          0 :     fjm2 = fjm1;
    1259                 :          0 :     fjm1 = fj;
    1260                 :          0 :     fj = fjp1;
    1261                 :          0 :     fjp1 = fjp2;
    1262                 :          0 :     fjp2 = fj + stridex2;
    1263                 :            :   }
    1264                 :            : 
    1265                 :          0 :   block_score = 0;
    1266         [ #  # ]:          0 :   for (i = 0; i < width / block_width; i++) {
    1267         [ #  # ]:          0 :     if (block_scores[i] > block_score)
    1268                 :          0 :       block_score = block_scores[i];
    1269                 :            :   }
    1270                 :            : 
    1271                 :          0 :   g_free (block_scores);
    1272                 :          0 :   g_free (comb_mask);
    1273                 :          0 :   return block_score;
    1274                 :            : }
    1275                 :            : 
    1276                 :            : /* a pass is made over the field using one of three comb-detection metrics
    1277                 :            :    and the results are then analysed block-wise. if the samples to the left
    1278                 :            :    and right are combed, they contribute to the block score. if the block
    1279                 :            :    score is above the given threshold, the frame is combed. if the block
    1280                 :            :    score is between half the threshold and the threshold, the block is
    1281                 :            :    slightly combed. if when analysis is complete, slight combing is detected
    1282                 :            :    that is returned. if any results are observed that are above the threshold,
    1283                 :            :    the function returns immediately */
    1284                 :            : /* 0th field's parity defines operation */
    1285                 :            : static gfloat
    1286                 :          0 : opposite_parity_windowed_comb (GstFieldAnalysis * filter,
    1287                 :            :     FieldAnalysisFields * fields)
    1288                 :            : {
    1289                 :            :   gint j;
    1290                 :            :   gboolean slightly_combed;
    1291                 :            : 
    1292                 :          0 :   const gint y_offset = filter->data_offset;
    1293                 :          0 :   const gint stride = filter->line_stride;
    1294                 :          0 :   const guint64 block_thresh = filter->block_thresh;
    1295                 :          0 :   const guint64 block_height = filter->block_height;
    1296                 :            :   guint8 *base_fj, *base_fjp1;
    1297                 :            : 
    1298         [ #  # ]:          0 :   if (fields[0].parity == TOP_FIELD) {
    1299                 :          0 :     base_fj = GST_BUFFER_DATA (fields[0].buf) + y_offset;
    1300                 :          0 :     base_fjp1 = GST_BUFFER_DATA (fields[1].buf) + y_offset + stride;
    1301                 :            :   } else {
    1302                 :          0 :     base_fj = GST_BUFFER_DATA (fields[1].buf) + y_offset;
    1303                 :          0 :     base_fjp1 = GST_BUFFER_DATA (fields[0].buf) + y_offset + stride;
    1304                 :            :   }
    1305                 :            : 
    1306                 :            :   /* we operate on a row of blocks of height block_height through each iteration */
    1307                 :          0 :   slightly_combed = FALSE;
    1308         [ #  # ]:          0 :   for (j = 0; j <= filter->height - filter->ignored_lines - block_height;
    1309                 :          0 :       j += block_height) {
    1310                 :          0 :     guint64 line_offset = (filter->ignored_lines + j) * stride;
    1311                 :          0 :     guint block_score =
    1312                 :          0 :         filter->block_score_for_row (filter, base_fj + line_offset,
    1313                 :            :         base_fjp1 + line_offset);
    1314                 :            : 
    1315         [ #  # ]:          0 :     if (block_score > (block_thresh >> 1)
    1316         [ #  # ]:          0 :         && block_score <= block_thresh) {
    1317                 :            :       /* blend if nothing more combed comes along */
    1318                 :          0 :       slightly_combed = TRUE;
    1319         [ #  # ]:          0 :     } else if (block_score > block_thresh) {
    1320                 :          0 :       GstCaps *caps = GST_BUFFER_CAPS (fields[0].buf);
    1321                 :          0 :       GstStructure *struc = gst_caps_get_structure (caps, 0);
    1322                 :            :       gboolean interlaced;
    1323         [ #  # ]:          0 :       if (gst_structure_get_boolean (struc, "interlaced", &interlaced)
    1324         [ #  # ]:          0 :           && interlaced == TRUE) {
    1325                 :          0 :         return 1.0f;            /* blend */
    1326                 :            :       } else {
    1327                 :          0 :         return 2.0f;            /* deinterlace */
    1328                 :            :       }
    1329                 :            :     }
    1330                 :            :   }
    1331                 :            : 
    1332                 :          0 :   return (gfloat) slightly_combed;      /* TRUE means blend, else don't */
    1333                 :            : }
    1334                 :            : 
    1335                 :            : /* this is where the magic happens
    1336                 :            :  *
    1337                 :            :  * the buffer incoming to the chain function (buf_to_queue) is added to the
    1338                 :            :  * internal queue and then should no longer be used until it is popped from the
    1339                 :            :  * queue.
    1340                 :            :  *
    1341                 :            :  * analysis is performed on the incoming buffer (peeked from the queue) and the
    1342                 :            :  * previous buffer using two classes of metrics making up five individual
    1343                 :            :  * scores.
    1344                 :            :  *
    1345                 :            :  * there are two same-parity comparisons: top of current with top of previous
    1346                 :            :  * and bottom of current with bottom of previous
    1347                 :            :  *
    1348                 :            :  * there are three opposing parity comparisons: top of current with bottom of
    1349                 :            :  * _current_, top of current with bottom of previous and bottom of current with
    1350                 :            :  * top of previous.
    1351                 :            :  *
    1352                 :            :  * from the results of these comparisons we can use some rather complex logic to
    1353                 :            :  * identify the state of the previous buffer, decorate and return it and
    1354                 :            :  * identify some preliminary state of the current buffer.
    1355                 :            :  *
    1356                 :            :  * the returned buffer has a ref on it (it has come from _make_metadata_writable
    1357                 :            :  * that was called on an incoming buffer that was queued and then popped) */
    1358                 :            : static GstBuffer *
    1359                 :          0 : gst_field_analysis_process_buffer (GstFieldAnalysis * filter,
    1360                 :            :     GstBuffer ** buf_to_queue)
    1361                 :            : {
    1362                 :            :   GQueue *queue;
    1363                 :            :   guint n_queued;
    1364                 :            :   /* res0/1 correspond to f0/1 */
    1365                 :            :   FieldAnalysis *res0, *res1;
    1366                 :            :   FieldAnalysisFields fields[2];
    1367                 :          0 :   GstBuffer *outbuf = NULL;
    1368                 :            : 
    1369                 :          0 :   queue = filter->frames;
    1370                 :            : 
    1371                 :            :   /* move previous result to res1 */
    1372                 :          0 :   filter->results[1] = filter->results[0];
    1373                 :            : 
    1374                 :          0 :   res0 = &filter->results[0];   /* results for current frame */
    1375                 :          0 :   res1 = &filter->results[1];   /* results for previous frame */
    1376                 :            : 
    1377                 :            :   /* we have a ref on buf_to_queue when it is added to the queue */
    1378                 :          0 :   g_queue_push_tail (queue, (gpointer) * buf_to_queue);
    1379                 :            :   /* WARNING: buf_to_queue must not be used again!!! */
    1380                 :          0 :   *buf_to_queue = NULL;
    1381                 :            : 
    1382                 :          0 :   n_queued = g_queue_get_length (queue);
    1383                 :            : 
    1384                 :            :   /* we do it like this because the first frame has no predecessor so this is
    1385                 :            :    * the only result we can get for it */
    1386         [ #  # ]:          0 :   if (n_queued >= 1) {
    1387                 :            :     /* compare the fields within the buffer, if the buffer exhibits combing it
    1388                 :            :      * could be interlaced or a mixed telecine frame */
    1389                 :          0 :     fields[0].buf = fields[1].buf = g_queue_peek_tail (queue);
    1390                 :          0 :     fields[0].parity = TOP_FIELD;
    1391                 :          0 :     fields[1].parity = BOTTOM_FIELD;
    1392                 :          0 :     res0->f = filter->same_frame (filter, fields);
    1393                 :          0 :     res0->t = res0->b = res0->t_b = res0->b_t = G_MAXINT64;
    1394         [ #  # ]:          0 :     if (n_queued == 1)
    1395         [ #  # ]:          0 :       GST_DEBUG_OBJECT (filter, "Scores: f %f, t , b , t_b , b_t ", res0->f);
    1396         [ #  # ]:          0 :     if (res0->f <= filter->frame_thresh) {
    1397                 :          0 :       res0->conclusion = FIELD_ANALYSIS_PROGRESSIVE;
    1398                 :            :     } else {
    1399                 :          0 :       res0->conclusion = FIELD_ANALYSIS_INTERLACED;
    1400                 :            :     }
    1401                 :          0 :     res0->holding = -1;         /* needed fields unknown */
    1402                 :          0 :     res0->gap = FALSE;
    1403                 :            :   }
    1404                 :            : 
    1405         [ #  # ]:          0 :   if (n_queued >= 2) {
    1406                 :            :     guint telecine_matches;
    1407                 :          0 :     gboolean first_buffer = filter->first_buffer;
    1408                 :            : 
    1409                 :          0 :     filter->first_buffer = FALSE;
    1410                 :            : 
    1411                 :          0 :     fields[1].buf = g_queue_peek_nth (queue, n_queued - 2);
    1412                 :            : 
    1413                 :            :     /* compare the top and bottom fields to the previous frame */
    1414                 :          0 :     fields[0].parity = TOP_FIELD;
    1415                 :          0 :     fields[1].parity = TOP_FIELD;
    1416                 :          0 :     res0->t = filter->same_field (filter, fields);
    1417                 :          0 :     fields[0].parity = BOTTOM_FIELD;
    1418                 :          0 :     fields[1].parity = BOTTOM_FIELD;
    1419                 :          0 :     res0->b = filter->same_field (filter, fields);
    1420                 :            : 
    1421                 :            :     /* compare the top field from this frame to the bottom of the previous for
    1422                 :            :      * for combing (and vice versa) */
    1423                 :          0 :     fields[0].parity = TOP_FIELD;
    1424                 :          0 :     fields[1].parity = BOTTOM_FIELD;
    1425                 :          0 :     res0->t_b = filter->same_frame (filter, fields);
    1426                 :          0 :     fields[0].parity = BOTTOM_FIELD;
    1427                 :          0 :     fields[1].parity = TOP_FIELD;
    1428                 :          0 :     res0->b_t = filter->same_frame (filter, fields);
    1429                 :            : 
    1430         [ #  # ]:          0 :     GST_DEBUG_OBJECT (filter,
    1431                 :            :         "Scores: f %f, t %f, b %f, t_b %f, b_t %f", res0->f,
    1432                 :            :         res0->t, res0->b, res0->t_b, res0->b_t);
    1433                 :            : 
    1434                 :            :     /* analysis */
    1435                 :          0 :     telecine_matches = 0;
    1436         [ #  # ]:          0 :     if (res0->t_b <= filter->frame_thresh)
    1437                 :          0 :       telecine_matches |= FIELD_ANALYSIS_TOP_BOTTOM;
    1438         [ #  # ]:          0 :     if (res0->b_t <= filter->frame_thresh)
    1439                 :          0 :       telecine_matches |= FIELD_ANALYSIS_BOTTOM_TOP;
    1440                 :            :     /* normally if there is a top or bottom field match, it is significantly
    1441                 :            :      * smaller than the other match - try 10% */
    1442 [ #  # ][ #  # ]:          0 :     if (res0->t <= filter->field_thresh || res0->t * (100 / 10) < res0->b)
    1443                 :          0 :       telecine_matches |= FIELD_ANALYSIS_TOP_MATCH;
    1444 [ #  # ][ #  # ]:          0 :     if (res0->b <= filter->field_thresh || res0->b * (100 / 10) < res0->t)
    1445                 :          0 :       telecine_matches |= FIELD_ANALYSIS_BOTTOM_MATCH;
    1446                 :            : 
    1447         [ #  # ]:          0 :     if (telecine_matches & (FIELD_ANALYSIS_TOP_MATCH |
    1448                 :            :             FIELD_ANALYSIS_BOTTOM_MATCH)) {
    1449                 :            :       /* we have a repeated field => some kind of telecine */
    1450         [ #  # ]:          0 :       if (res1->f <= filter->frame_thresh) {
    1451                 :            :         /* prev P */
    1452         [ #  # ]:          0 :         if ((telecine_matches & FIELD_ANALYSIS_TOP_MATCH)
    1453         [ #  # ]:          0 :             && (telecine_matches & FIELD_ANALYSIS_BOTTOM_MATCH)) {
    1454                 :            :           /* prev P, cur repeated => cur P */
    1455                 :          0 :           res0->conclusion = FIELD_ANALYSIS_TELECINE_PROGRESSIVE;
    1456                 :          0 :           res0->holding = 1 + BOTH_FIELDS;
    1457                 :            :           /* push prev P, GAP */
    1458                 :          0 :           res1->gap = TRUE;
    1459                 :          0 :           outbuf =
    1460                 :          0 :               gst_field_analysis_decorate (filter, -1, FALSE, res1->conclusion,
    1461                 :            :               res1->gap);
    1462                 :            :         } else {
    1463                 :            :           /* prev P, cur t xor b matches => cur TCM */
    1464                 :          0 :           res0->conclusion = FIELD_ANALYSIS_TELECINE_MIXED;
    1465                 :            :           /* hold non-repeated: if bottom match, hold top = 1 + 0 */
    1466         [ #  # ]:          0 :           res0->holding = 1 + !(telecine_matches & FIELD_ANALYSIS_BOTTOM_MATCH);
    1467                 :            :           /* push prev P */
    1468                 :          0 :           outbuf =
    1469                 :          0 :               gst_field_analysis_decorate (filter, -1, FALSE, res1->conclusion,
    1470                 :            :               res1->gap);
    1471                 :            :         }
    1472                 :            :       } else {
    1473                 :            :         /* prev !P */
    1474                 :            :         gboolean b, t;
    1475                 :            : 
    1476         [ #  # ]:          0 :         if (res0->f <= filter->frame_thresh) {
    1477                 :            :           /* cur P */
    1478                 :          0 :           res0->conclusion = FIELD_ANALYSIS_TELECINE_PROGRESSIVE;
    1479                 :          0 :           res0->holding = 1 + BOTH_FIELDS;
    1480                 :            :         } else {
    1481                 :            :           /* cur !P */
    1482                 :          0 :           res0->conclusion = FIELD_ANALYSIS_TELECINE_MIXED;
    1483         [ #  # ]:          0 :           if (telecine_matches & FIELD_ANALYSIS_TOP_MATCH
    1484         [ #  # ]:          0 :               && telecine_matches & FIELD_ANALYSIS_BOTTOM_MATCH) {
    1485                 :            :             /* cur t && b */
    1486                 :          0 :             res0->holding = 0;
    1487                 :            :           } else {
    1488                 :            :             /* cur t xor b; hold non-repeated */
    1489                 :          0 :             res0->holding =
    1490         [ #  # ]:          0 :                 1 + !(telecine_matches & FIELD_ANALYSIS_BOTTOM_MATCH);
    1491                 :            :           }
    1492                 :            :         }
    1493                 :            : 
    1494         [ #  # ]:          0 :         if (res1->holding == -1) {
    1495                 :          0 :           b = t = TRUE;
    1496                 :            :         } else {
    1497                 :          0 :           b = res1->holding == 1 + BOTTOM_FIELD;
    1498                 :          0 :           t = res1->holding == 1 + TOP_FIELD;
    1499                 :            :         }
    1500                 :            : 
    1501 [ #  # ][ #  # ]:          0 :         if ((t && telecine_matches & FIELD_ANALYSIS_BOTTOM_MATCH) || (b
                 [ #  # ]
    1502         [ #  # ]:          0 :                 && telecine_matches & FIELD_ANALYSIS_TOP_MATCH)) {
    1503 [ #  # ][ #  # ]:          0 :           if (t && telecine_matches & FIELD_ANALYSIS_BOTTOM_MATCH) {
    1504                 :          0 :             res1->holding = 1 + TOP_FIELD;
    1505 [ #  # ][ #  # ]:          0 :           } else if (b && telecine_matches & FIELD_ANALYSIS_TOP_MATCH) {
    1506                 :          0 :             res1->holding = 1 + BOTTOM_FIELD;
    1507                 :            :           }
    1508                 :            :           /* push 1F held field */
    1509                 :          0 :           outbuf =
    1510                 :          0 :               gst_field_analysis_decorate (filter, !(res1->holding - 1), TRUE,
    1511                 :            :               res1->conclusion, res1->gap);
    1512 [ #  # ][ #  # ]:          0 :         } else if (res0->f > filter->frame_thresh && ((t
    1513 [ #  # ][ #  # ]:          0 :                     && telecine_matches & FIELD_ANALYSIS_BOTTOM_TOP) || (b
    1514         [ #  # ]:          0 :                     && telecine_matches & FIELD_ANALYSIS_TOP_BOTTOM))) {
    1515 [ #  # ][ #  # ]:          0 :           if (t && telecine_matches & FIELD_ANALYSIS_BOTTOM_TOP) {
    1516                 :          0 :             res1->holding = 1 + TOP_FIELD;
    1517 [ #  # ][ #  # ]:          0 :           } else if (b && telecine_matches & FIELD_ANALYSIS_TOP_BOTTOM) {
    1518                 :          0 :             res1->holding = 1 + BOTTOM_FIELD;
    1519                 :            :           }
    1520                 :          0 :           res0->conclusion = FIELD_ANALYSIS_TELECINE_MIXED;
    1521                 :            :           /* hold the opposite field to the one held in the last frame */
    1522         [ #  # ]:          0 :           res0->holding = 1 + (res1->holding == 1 + TOP_FIELD);
    1523                 :            :           /* push 1F held field */
    1524                 :          0 :           outbuf =
    1525                 :          0 :               gst_field_analysis_decorate (filter, !(res1->holding - 1), TRUE,
    1526                 :            :               res1->conclusion, res1->gap);
    1527 [ #  # ][ #  # ]:          0 :         } else if (first_buffer && (telecine_matches & FIELD_ANALYSIS_BOTTOM_TOP
    1528         [ #  # ]:          0 :                 || telecine_matches & FIELD_ANALYSIS_TOP_BOTTOM)) {
    1529                 :            :           /* non-matched field is an orphan in the first buffer - push orphan as 1F */
    1530                 :          0 :           res1->conclusion = FIELD_ANALYSIS_TELECINE_MIXED;
    1531                 :            :           /* if prev b matched, prev t is orphan */
    1532         [ #  # ]:          0 :           res1->holding = 1 + !(telecine_matches & FIELD_ANALYSIS_TOP_BOTTOM);
    1533                 :            :           /* push 1F held field */
    1534                 :          0 :           outbuf =
    1535                 :          0 :               gst_field_analysis_decorate (filter, !(res1->holding - 1), TRUE,
    1536                 :            :               res1->conclusion, res1->gap);
    1537 [ #  # ][ #  # ]:          0 :         } else if (res1->holding == 1 + BOTH_FIELDS || res1->holding == -1) {
    1538                 :            :           /* holding both fields, push prev as is */
    1539                 :          0 :           outbuf =
    1540                 :          0 :               gst_field_analysis_decorate (filter, -1, FALSE, res1->conclusion,
    1541                 :            :               res1->gap);
    1542                 :            :         } else {
    1543                 :            :           /* push prev as is with GAP */
    1544                 :          0 :           res1->gap = TRUE;
    1545                 :          0 :           outbuf =
    1546                 :          0 :               gst_field_analysis_decorate (filter, -1, FALSE, res1->conclusion,
    1547                 :            :               res1->gap);
    1548                 :            :         }
    1549                 :            :       }
    1550         [ #  # ]:          0 :     } else if (res0->f <= filter->frame_thresh) {
    1551                 :            :       /* cur P */
    1552                 :          0 :       res0->conclusion = FIELD_ANALYSIS_PROGRESSIVE;
    1553                 :          0 :       res0->holding = 1 + BOTH_FIELDS;
    1554 [ #  # ][ #  # ]:          0 :       if (res1->holding == 1 + BOTH_FIELDS || res1->holding == -1) {
    1555                 :            :         /* holding both fields, push prev as is */
    1556                 :          0 :         outbuf =
    1557                 :          0 :             gst_field_analysis_decorate (filter, -1, FALSE, res1->conclusion,
    1558                 :            :             res1->gap);
    1559         [ #  # ]:          0 :       } else if (res1->holding > 0) {
    1560                 :            :         /* holding one field, push prev 1F held */
    1561                 :          0 :         outbuf =
    1562                 :          0 :             gst_field_analysis_decorate (filter, !(res1->holding - 1), TRUE,
    1563                 :            :             res1->conclusion, res1->gap);
    1564                 :            :       } else {
    1565                 :            :         /* unknown or no fields held, push prev as is with GAP */
    1566                 :            :         /* this will push unknown as gap - should be pushed as not gap? */
    1567                 :          0 :         res1->gap = TRUE;
    1568                 :          0 :         outbuf =
    1569                 :          0 :             gst_field_analysis_decorate (filter, -1, FALSE, res1->conclusion,
    1570                 :            :             res1->gap);
    1571                 :            :       }
    1572                 :            :     } else {
    1573                 :            :       /* cur !P */
    1574         [ #  # ]:          0 :       if (telecine_matches & (FIELD_ANALYSIS_TOP_BOTTOM |
    1575                 :            :               FIELD_ANALYSIS_BOTTOM_TOP)) {
    1576                 :            :         /* cross-parity match => TCM */
    1577                 :            :         gboolean b, t;
    1578                 :            : 
    1579         [ #  # ]:          0 :         if (res1->holding == -1) {
    1580                 :          0 :           b = t = TRUE;
    1581                 :            :         } else {
    1582                 :          0 :           b = res1->holding == 1 + BOTTOM_FIELD;
    1583                 :          0 :           t = res1->holding == 1 + TOP_FIELD;
    1584                 :            :         }
    1585                 :            : 
    1586                 :          0 :         res0->conclusion = FIELD_ANALYSIS_TELECINE_MIXED;
    1587                 :            :         /* leave holding as unknown */
    1588         [ #  # ]:          0 :         if (res1->holding == 1 + BOTH_FIELDS) {
    1589                 :            :           /* prev P/TCP/I [or TCM repeated (weird case)] */
    1590                 :            :           /* push prev as is */
    1591                 :          0 :           outbuf =
    1592                 :          0 :               gst_field_analysis_decorate (filter, -1, FALSE, res1->conclusion,
    1593                 :            :               res1->gap);
    1594 [ #  # ][ #  # ]:          0 :         } else if ((t && telecine_matches & FIELD_ANALYSIS_TOP_BOTTOM) || (b
                 [ #  # ]
    1595         [ #  # ]:          0 :                 && telecine_matches & FIELD_ANALYSIS_BOTTOM_TOP)) {
    1596                 :            :           /* held is opposite to matched => need both field from prev */
    1597                 :            :           /* if t_b, hold bottom from prev and top from current, else vice-versa */
    1598                 :          0 :           res1->holding = 1 + ! !(telecine_matches & FIELD_ANALYSIS_TOP_BOTTOM);
    1599         [ #  # ]:          0 :           res0->holding = 1 + !(telecine_matches & FIELD_ANALYSIS_TOP_BOTTOM);
    1600                 :            :           /* push prev TCM */
    1601                 :          0 :           outbuf =
    1602                 :          0 :               gst_field_analysis_decorate (filter, -1, FALSE, res1->conclusion,
    1603                 :            :               res1->gap);
    1604 [ #  # ][ #  # ]:          0 :         } else if ((res1->holding > 0 && res1->holding != 1 + BOTH_FIELDS) || (t
                 [ #  # ]
    1605 [ #  # ][ #  # ]:          0 :                 && telecine_matches & FIELD_ANALYSIS_BOTTOM_TOP) || (b
    1606         [ #  # ]:          0 :                 && telecine_matches & FIELD_ANALYSIS_TOP_BOTTOM)) {
    1607                 :            :           /* held field is needed, push prev 1F held */
    1608                 :          0 :           outbuf =
    1609                 :          0 :               gst_field_analysis_decorate (filter, !(res1->holding - 1), TRUE,
    1610                 :            :               res1->conclusion, res1->gap);
    1611                 :            :         } else {
    1612                 :            :           /* holding none or unknown */
    1613                 :            :           /* push prev as is with GAP */
    1614                 :          0 :           res1->gap = TRUE;
    1615                 :          0 :           outbuf =
    1616                 :          0 :               gst_field_analysis_decorate (filter, -1, FALSE, res1->conclusion,
    1617                 :            :               res1->gap);
    1618                 :            :         }
    1619                 :            :       } else {
    1620                 :            :         /* cur I */
    1621                 :          0 :         res0->conclusion = FIELD_ANALYSIS_INTERLACED;
    1622                 :          0 :         res0->holding = 1 + BOTH_FIELDS;
    1623                 :            :         /* push prev appropriately */
    1624                 :          0 :         res1->gap = res1->holding <= 0;
    1625         [ #  # ]:          0 :         if (res1->holding != 0) {
    1626                 :          0 :           res1->gap = FALSE;
    1627 [ #  # ][ #  # ]:          0 :           if (res1->holding == 1 + BOTH_FIELDS || res1->holding == -1) {
    1628                 :            :             /* push prev as is */
    1629                 :          0 :             outbuf =
    1630                 :          0 :                 gst_field_analysis_decorate (filter, -1, FALSE,
    1631                 :            :                 res1->conclusion, res1->gap);
    1632                 :            :           } else {
    1633                 :            :             /* push prev 1F held */
    1634                 :          0 :             outbuf =
    1635                 :          0 :                 gst_field_analysis_decorate (filter, !(res1->holding - 1), TRUE,
    1636                 :            :                 res1->conclusion, res1->gap);
    1637                 :            :           }
    1638                 :            :         } else {
    1639                 :            :           /* push prev as is with GAP */
    1640                 :          0 :           res1->gap = TRUE;
    1641                 :          0 :           outbuf =
    1642                 :          0 :               gst_field_analysis_decorate (filter, -1, FALSE, res1->conclusion,
    1643                 :            :               res1->gap);
    1644                 :            :         }
    1645                 :            :       }
    1646                 :            :     }
    1647                 :            :   }
    1648                 :            : 
    1649   [ #  #  #  #  :          0 :   switch (res0->conclusion) {
                      # ]
    1650                 :            :     case FIELD_ANALYSIS_PROGRESSIVE:
    1651         [ #  # ]:          0 :       GST_DEBUG_OBJECT (filter, "Conclusion: PROGRESSIVE");
    1652                 :          0 :       break;
    1653                 :            :     case FIELD_ANALYSIS_INTERLACED:
    1654         [ #  # ]:          0 :       GST_DEBUG_OBJECT (filter, "Conclusion: INTERLACED");
    1655                 :          0 :       break;
    1656                 :            :     case FIELD_ANALYSIS_TELECINE_PROGRESSIVE:
    1657         [ #  # ]:          0 :       GST_DEBUG_OBJECT (filter, "Conclusion: TC PROGRESSIVE");
    1658                 :          0 :       break;
    1659                 :            :     case FIELD_ANALYSIS_TELECINE_MIXED:
    1660 [ #  # ][ #  # ]:          0 :       GST_DEBUG_OBJECT (filter, "Conclusion: TC MIXED %s",
                 [ #  # ]
    1661                 :            :           res0->holding ==
    1662                 :            :           1 + BOTH_FIELDS ? "top and bottom" : res0->holding ==
    1663                 :            :           1 + BOTTOM_FIELD ? "bottom" : "top");
    1664                 :          0 :       break;
    1665                 :            :     default:
    1666         [ #  # ]:          0 :       GST_DEBUG_OBJECT (filter, "Invalid conclusion! This is a bug!");
    1667                 :          0 :       break;
    1668                 :            :   }
    1669                 :            : 
    1670         [ #  # ]:          0 :   GST_DEBUG_OBJECT (filter, "Items remaining in the queue: %d",
    1671                 :            :       g_queue_get_length (queue));
    1672                 :            : 
    1673                 :          0 :   return outbuf;
    1674                 :            : }
    1675                 :            : 
    1676                 :            : /* we have a ref on buf when it comes into chain */
    1677                 :            : static GstFlowReturn
    1678                 :          0 : gst_field_analysis_chain (GstPad * pad, GstBuffer * buf)
    1679                 :            : {
    1680                 :          0 :   GstFlowReturn ret = GST_FLOW_OK;
    1681                 :            :   GstFieldAnalysis *filter;
    1682                 :          0 :   GstBuffer *outbuf = NULL;
    1683                 :            : 
    1684                 :          0 :   filter = GST_FIELDANALYSIS (GST_OBJECT_PARENT (pad));
    1685                 :            : 
    1686                 :          0 :   GST_OBJECT_LOCK (filter);
    1687         [ #  # ]:          0 :   if (filter->flushing) {
    1688         [ #  # ]:          0 :     GST_DEBUG_OBJECT (filter, "We are flushing.");
    1689                 :            :     /* we have a ref on buf so it must be unreffed */
    1690                 :          0 :     goto unref_unlock_ret;
    1691                 :            :   }
    1692                 :            : 
    1693         [ #  # ]:          0 :   if (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DISCONT)) {
    1694         [ #  # ]:          0 :     GST_DEBUG_OBJECT (filter, "Discont: flushing");
    1695                 :            :     /* we should have a ref on outbuf, either because we had one when it entered
    1696                 :            :      * the queue and _make_metadata_writable () inside _decorate () returned
    1697                 :            :      * the same buffer or because it returned a new buffer on which we have one
    1698                 :            :      * ref */
    1699                 :          0 :     outbuf = gst_field_analysis_flush_one (filter, NULL);
    1700                 :            : 
    1701         [ #  # ]:          0 :     if (outbuf) {
    1702                 :            :       /* we give away our ref on outbuf here */
    1703                 :          0 :       GST_OBJECT_UNLOCK (filter);
    1704                 :          0 :       ret = gst_pad_push (filter->srcpad, outbuf);
    1705                 :          0 :       GST_OBJECT_LOCK (filter);
    1706         [ #  # ]:          0 :       if (filter->flushing) {
    1707         [ #  # ]:          0 :         GST_DEBUG_OBJECT (filter, "We are flushing. outbuf already pushed.");
    1708                 :            :         /* we have a ref on buf so it must be unreffed */
    1709                 :          0 :         goto unref_unlock_ret;
    1710                 :            :       }
    1711                 :            :     }
    1712                 :            : 
    1713                 :          0 :     gst_field_analysis_reset (filter);
    1714                 :            : 
    1715         [ #  # ]:          0 :     if (ret != GST_FLOW_OK) {
    1716         [ #  # ]:          0 :       GST_DEBUG_OBJECT (filter,
    1717                 :            :           "Pushing of flushed buffer failed with return %d", ret);
    1718                 :            :       /* we have a ref on buf so it must be unreffed */
    1719                 :          0 :       goto unref_unlock_ret;
    1720                 :            :     } else {
    1721                 :          0 :       outbuf = NULL;
    1722                 :            :     }
    1723                 :            :   }
    1724                 :            : 
    1725                 :            :   /* after this function, buf has been pushed to the internal queue and its ref
    1726                 :            :    * retained there and we have a ref on outbuf */
    1727                 :          0 :   outbuf = gst_field_analysis_process_buffer (filter, &buf);
    1728                 :            : 
    1729                 :          0 :   GST_OBJECT_UNLOCK (filter);
    1730                 :            : 
    1731                 :            :   /* here we give up our ref on outbuf */
    1732         [ #  # ]:          0 :   if (outbuf)
    1733                 :          0 :     ret = gst_pad_push (filter->srcpad, outbuf);
    1734                 :            : 
    1735                 :          0 :   return ret;
    1736                 :            : 
    1737                 :            : unref_unlock_ret:
    1738                 :            :   /* we must unref the input buffer here */
    1739                 :          0 :   gst_buffer_unref (buf);
    1740                 :          0 :   GST_OBJECT_UNLOCK (filter);
    1741                 :          0 :   return ret;
    1742                 :            : }
    1743                 :            : 
    1744                 :            : static GstStateChangeReturn
    1745                 :         34 : gst_field_analysis_change_state (GstElement * element,
    1746                 :            :     GstStateChange transition)
    1747                 :            : {
    1748                 :            :   GstStateChangeReturn ret;
    1749                 :         34 :   GstFieldAnalysis *filter = GST_FIELDANALYSIS (element);
    1750                 :            : 
    1751   [ +  +  +  + ]:         34 :   switch (transition) {
    1752                 :            :     case GST_STATE_CHANGE_NULL_TO_READY:
    1753                 :          4 :       break;
    1754                 :            :     case GST_STATE_CHANGE_READY_TO_PAUSED:
    1755                 :          7 :       break;
    1756                 :            :     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
    1757                 :          6 :       break;
    1758                 :            :     default:
    1759                 :         17 :       break;
    1760                 :            :   }
    1761                 :            : 
    1762                 :         34 :   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
    1763         [ -  + ]:         34 :   if (ret != GST_STATE_CHANGE_SUCCESS)
    1764                 :          0 :     return ret;
    1765                 :            : 
    1766      [ +  +  + ]:         34 :   switch (transition) {
    1767                 :            :     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
    1768                 :          6 :       break;
    1769                 :            :     case GST_STATE_CHANGE_PAUSED_TO_READY:
    1770                 :          7 :       gst_field_analysis_reset (filter);
    1771                 :          7 :       break;
    1772                 :            :     case GST_STATE_CHANGE_READY_TO_NULL:
    1773                 :            :     default:
    1774                 :         21 :       break;
    1775                 :            :   }
    1776                 :            : 
    1777                 :         34 :   return ret;
    1778                 :            : }
    1779                 :            : 
    1780                 :            : static void
    1781                 :          4 : gst_field_analysis_finalize (GObject * object)
    1782                 :            : {
    1783                 :          4 :   GstFieldAnalysis *filter = GST_FIELDANALYSIS (object);
    1784                 :            : 
    1785                 :          4 :   gst_field_analysis_reset (filter);
    1786                 :          4 :   g_queue_free (filter->frames);
    1787                 :            : 
    1788                 :          4 :   G_OBJECT_CLASS (parent_class)->finalize (object);
    1789                 :          4 : }
    1790                 :            : 
    1791                 :            : 
    1792                 :            : static gboolean
    1793                 :          6 : fieldanalysis_init (GstPlugin * fieldanalysis)
    1794                 :            : {
    1795         [ +  - ]:          6 :   GST_DEBUG_CATEGORY_INIT (gst_field_analysis_debug, "fieldanalysis",
    1796                 :            :       0, "Video field analysis");
    1797                 :            : 
    1798                 :          6 :   gst_fieldanalysis_orc_init ();
    1799                 :            : 
    1800                 :          6 :   return gst_element_register (fieldanalysis, "fieldanalysis", GST_RANK_NONE,
    1801                 :            :       GST_TYPE_FIELDANALYSIS);
    1802                 :            : }
    1803                 :            : 
    1804                 :            : GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
    1805                 :            :     GST_VERSION_MINOR,
    1806                 :            :     "fieldanalysis",
    1807                 :            :     "Video field analysis",
    1808                 :            :     fieldanalysis_init, VERSION, "LGPL", "GStreamer", "http://gstreamer.net/")

Generated by: LCOV version 1.9