• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer
2  * Copyright (C) 2010 David A. Schleef <ds@schleef.org>
3  * Copyright (C) 2010 Robert Swain <robert.swain@collabora.co.uk>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 /**
21  * SECTION:element-interlace
22  * @title: interlace
23  *
24  * The interlace element takes a non-interlaced raw video stream as input,
25  * creates fields out of each frame, then combines fields into interlaced
26  * frames to output as an interlaced video stream. It can also produce
27  * telecined streams from progressive input.
28  *
29  * ## Example launch line
30  * |[
31  * gst-launch-1.0 -v videotestsrc pattern=ball ! interlace ! xvimagesink
32  * ]|
33  * This pipeline illustrates the combing effects caused by displaying
34  * two interlaced fields as one progressive frame.
35  * |[
36  * gst-launch-1.0 -v filesrc location=/path/to/file ! decodebin ! videorate !
37  *   videoscale ! video/x-raw,format=\(string\)I420,width=720,height=480,
38  *   framerate=60000/1001,pixel-aspect-ratio=11/10 !
39  *   interlace top-field-first=false ! autovideosink
40  * ]|
41  * This pipeline converts a progressive video stream into an interlaced
42  * stream suitable for standard definition NTSC.
43  * |[
44  * gst-launch-1.0 -v videotestsrc pattern=ball ! video/x-raw,
45  *   format=\(string\)I420,width=720,height=480,framerate=24000/1001,
46  *   pixel-aspect-ratio=11/10 ! interlace !
47  *   autovideosink
48  * ]|
49  * This pipeline converts a 24 frames per second progressive film stream into a
50  * 30000/1001 2:3:2:3... pattern telecined stream suitable for displaying film
51  * content on NTSC.
52  *
53  */
54 
55 
56 #ifdef HAVE_CONFIG_H
57 #include "config.h"
58 #endif
59 
60 #include <gst/gst.h>
61 #include <gst/video/video.h>
62 #include <stdlib.h>
63 #include <string.h>
64 #include <math.h>
65 
66 GST_DEBUG_CATEGORY (gst_interlace_debug);
67 #define GST_CAT_DEFAULT gst_interlace_debug
68 
69 #define GST_TYPE_INTERLACE \
70   (gst_interlace_get_type())
71 #define GST_INTERLACE(obj) \
72   (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_INTERLACE,GstInterlace))
73 #define GST_INTERLACE_DEC_CLASS(klass) \
74   (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_INTERLACE,GstInterlaceClass))
75 #define GST_IS_GST_INTERLACE(obj) \
76   (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_INTERLACE))
77 #define GST_IS_GST_INTERLACE_CLASS(obj) \
78   (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_INTERLACE))
79 
80 typedef struct _GstInterlace GstInterlace;
81 typedef struct _GstInterlaceClass GstInterlaceClass;
82 
83 struct _GstInterlace
84 {
85   GstElement element;
86 
87   GstPad *srcpad;
88   GstPad *sinkpad;
89 
90   /* properties */
91   gboolean top_field_first;
92   gint pattern;
93   gboolean allow_rff;
94 
95   /* state */
96   GstVideoInfo info;
97   GstVideoInfo out_info;
98   int src_fps_n;
99   int src_fps_d;
100 
101   gint new_pattern;
102   GstBuffer *stored_frame;
103   guint stored_fields;
104   guint phase_index;
105   guint field_index;            /* index of the next field to push, 0=top 1=bottom */
106   GstClockTime timebase;
107   guint fields_since_timebase;
108   guint pattern_offset;         /* initial offset into the pattern */
109   gboolean passthrough;
110   gboolean switch_fields;
111 };
112 
113 struct _GstInterlaceClass
114 {
115   GstElementClass element_class;
116 };
117 
118 enum
119 {
120   PROP_0,
121   PROP_TOP_FIELD_FIRST,
122   PROP_PATTERN,
123   PROP_PATTERN_OFFSET,
124   PROP_ALLOW_RFF
125 };
126 
127 typedef enum
128 {
129   GST_INTERLACE_PATTERN_1_1,
130   GST_INTERLACE_PATTERN_2_2,
131   GST_INTERLACE_PATTERN_2_3,
132   GST_INTERLACE_PATTERN_2_3_3_2,
133   GST_INTERLACE_PATTERN_EURO,
134   GST_INTERLACE_PATTERN_3_4R3,
135   GST_INTERLACE_PATTERN_3R7_4,
136   GST_INTERLACE_PATTERN_3_3_4,
137   GST_INTERLACE_PATTERN_3_3,
138   GST_INTERLACE_PATTERN_3_2R4,
139   GST_INTERLACE_PATTERN_1_2R4,
140 } GstInterlacePattern;
141 
142 #define GST_INTERLACE_PATTERN (gst_interlace_pattern_get_type ())
143 static GType
gst_interlace_pattern_get_type(void)144 gst_interlace_pattern_get_type (void)
145 {
146   static GType interlace_pattern_type = 0;
147   static const GEnumValue pattern_types[] = {
148     {GST_INTERLACE_PATTERN_1_1, "1:1 (e.g. 60p -> 60i)", "1:1"},
149     {GST_INTERLACE_PATTERN_2_2, "2:2 (e.g. 30p -> 60i)", "2:2"},
150     {GST_INTERLACE_PATTERN_2_3, "2:3 (e.g. 24p -> 60i telecine)", "2:3"},
151     {GST_INTERLACE_PATTERN_2_3_3_2, "2:3:3:2 (e.g. 24p -> 60i telecine)",
152         "2:3:3:2"},
153     {GST_INTERLACE_PATTERN_EURO, "Euro 2-11:3 (e.g. 24p -> 50i telecine)",
154         "2-11:3"},
155     {GST_INTERLACE_PATTERN_3_4R3, "3:4-3 (e.g. 16p -> 60i telecine)", "3:4-3"},
156     {GST_INTERLACE_PATTERN_3R7_4, "3-7:4 (e.g. 16p -> 50i telecine)", "3-7:4"},
157     {GST_INTERLACE_PATTERN_3_3_4, "3:3:4 (e.g. 18p -> 60i telecine)", "3:3:4"},
158     {GST_INTERLACE_PATTERN_3_3, "3:3 (e.g. 20p -> 60i telecine)", "3:3"},
159     {GST_INTERLACE_PATTERN_3_2R4, "3:2-4 (e.g. 27.5p -> 60i telecine)",
160         "3:2-4"},
161     {GST_INTERLACE_PATTERN_1_2R4, "1:2-4 (e.g. 27.5p -> 50i telecine)",
162         "1:2-4"},
163     {0, NULL, NULL}
164   };
165 
166   if (!interlace_pattern_type) {
167     interlace_pattern_type =
168         g_enum_register_static ("GstInterlacePattern", pattern_types);
169   }
170 
171   return interlace_pattern_type;
172 }
173 
174 /* We can support all planar and packed YUV formats, but not tiled formats.
175  * We don't advertise RGB formats because interlaced video is usually YUV. */
176 #define VIDEO_FORMATS \
177   "{" \
178   "AYUV64, "                                                               /* 16-bit 4:4:4:4 */ \
179   "Y412_BE, Y412_LE, "                                                     /* 12-bit 4:4:4:4 */ \
180   "A444_10BE,A444_10LE, "                                                  /* 10-bit 4:4:4:4 */ \
181   "AYUV, VUYA, "                                                           /*  8-bit 4:4:4:4 */ \
182   "A422_10BE, A422_10LE, "                                                 /* 10-bit 4:4:2:2 */ \
183   "A420_10BE, A420_10LE, "                                                 /* 10-bit 4:4:2:0 */ \
184   "A420, "                                                                 /*  8-bit 4:4:2:0 */ \
185   "Y444_16BE, Y444_16LE, "                                                 /* 16-bit 4:4:4 */ \
186   "Y444_12BE, Y444_12LE, "                                                 /* 12-bit 4:4:4 */ \
187   "Y410, Y444_10BE, Y444_10LE, "                                           /* 10-bit 4:4:4 */ \
188   "v308, IYU2, Y444, NV24, "                                               /*  8-bit 4:4:4 */ \
189   "v216, I422_12BE, I422_12LE, "                                           /* 16-bit 4:2:2 */ \
190   "Y212_BE, Y212_LE, "                                                     /* 12-bit 4:2:2 */ \
191   "UYVP, Y210, NV16_10LE32, v210, I422_10BE, I422_10LE, "                  /* 10-bit 4:2:2 */ \
192   "YUY2, UYVY, VYUY, YVYU, Y42B, NV16, NV61, "                             /*  8-bit 4:2:2 */ \
193   "P016_BE, P016_LE, "                                                     /* 16-bit 4:2:0 */ \
194   "I420_12BE, I420_12LE, P012_BE, P012_LE, "                               /* 12-bit 4:2:0 */ \
195   "NV12_10LE40, NV12_10LE32, I420_10BE, I420_10LE, P010_10BE, P010_10LE, " /* 10-bit 4:2:0 */ \
196   "I420, YV12, NV12, NV21, "                                               /*  8-bit 4:2:0 */ \
197   "IYU1, Y41B, "                                                           /*  8-bit 4:1:1 */ \
198   "YUV9, YVU9, "                                                           /*  8-bit 4:1:0 */ \
199   "}"
200 
201 static GstStaticPadTemplate gst_interlace_src_template =
202     GST_STATIC_PAD_TEMPLATE ("src",
203     GST_PAD_SRC,
204     GST_PAD_ALWAYS,
205     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (VIDEO_FORMATS)
206         ",interlace-mode={interleaved,mixed} ;"
207         GST_VIDEO_CAPS_MAKE_WITH_FEATURES (GST_CAPS_FEATURE_FORMAT_INTERLACED,
208             VIDEO_FORMATS)
209         ",interlace-mode=alternate")
210     );
211 
212 static GstStaticPadTemplate gst_interlace_sink_template =
213     GST_STATIC_PAD_TEMPLATE ("sink",
214     GST_PAD_SINK,
215     GST_PAD_ALWAYS,
216     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (VIDEO_FORMATS)
217         ",interlace-mode=progressive ;" GST_VIDEO_CAPS_MAKE (VIDEO_FORMATS)
218         ",interlace-mode=interleaved,field-order={top-field-first,bottom-field-first}; "
219         GST_VIDEO_CAPS_MAKE (VIDEO_FORMATS) ",interlace-mode=mixed ;"
220         GST_VIDEO_CAPS_MAKE_WITH_FEATURES (GST_CAPS_FEATURE_FORMAT_INTERLACED,
221             VIDEO_FORMATS)
222         ",interlace-mode=alternate")
223     );
224 
225 GType gst_interlace_get_type (void);
226 static void gst_interlace_finalize (GObject * obj);
227 
228 static void gst_interlace_set_property (GObject * object,
229     guint prop_id, const GValue * value, GParamSpec * pspec);
230 static void gst_interlace_get_property (GObject * object,
231     guint prop_id, GValue * value, GParamSpec * pspec);
232 
233 static gboolean gst_interlace_sink_event (GstPad * pad, GstObject * parent,
234     GstEvent * event);
235 static gboolean gst_interlace_sink_query (GstPad * pad, GstObject * parent,
236     GstQuery * query);
237 static GstFlowReturn gst_interlace_chain (GstPad * pad, GstObject * parent,
238     GstBuffer * buffer);
239 
240 static gboolean gst_interlace_src_query (GstPad * pad, GstObject * parent,
241     GstQuery * query);
242 
243 static GstStateChangeReturn gst_interlace_change_state (GstElement * element,
244     GstStateChange transition);
245 
246 static GstCaps *gst_interlace_caps_double_framerate (GstCaps * caps,
247     gboolean half, gboolean skip_progressive);
248 
249 GST_ELEMENT_REGISTER_DECLARE (interlace);
250 
251 #define gst_interlace_parent_class parent_class
252 G_DEFINE_TYPE (GstInterlace, gst_interlace, GST_TYPE_ELEMENT);
253 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (interlace, "interlace", GST_RANK_NONE,
254     GST_TYPE_INTERLACE, GST_DEBUG_CATEGORY_INIT (gst_interlace_debug,
255         "interlace", 0, "interlace element"));
256 static void
gst_interlace_class_init(GstInterlaceClass * klass)257 gst_interlace_class_init (GstInterlaceClass * klass)
258 {
259   GObjectClass *object_class = G_OBJECT_CLASS (klass);
260   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
261 
262   parent_class = g_type_class_peek_parent (klass);
263 
264   object_class->set_property = gst_interlace_set_property;
265   object_class->get_property = gst_interlace_get_property;
266   object_class->finalize = gst_interlace_finalize;
267 
268   g_object_class_install_property (object_class, PROP_TOP_FIELD_FIRST,
269       g_param_spec_boolean ("top-field-first", "top field first",
270           "Interlaced stream should be top field first", FALSE,
271           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
272 
273   g_object_class_install_property (object_class, PROP_PATTERN,
274       g_param_spec_enum ("field-pattern", "Field pattern",
275           "The output field pattern", GST_INTERLACE_PATTERN,
276           GST_INTERLACE_PATTERN_2_3,
277           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
278 
279   g_object_class_install_property (object_class, PROP_PATTERN_OFFSET,
280       g_param_spec_uint ("pattern-offset", "Pattern offset",
281           "The initial field pattern offset. Counts from 0.",
282           0, 12, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
283 
284   g_object_class_install_property (object_class, PROP_ALLOW_RFF,
285       g_param_spec_boolean ("allow-rff", "Allow Repeat-First-Field flags",
286           "Allow generation of buffers with RFF flag set, i.e., duration of 3 fields",
287           FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
288 
289   gst_element_class_set_static_metadata (element_class,
290       "Interlace filter", "Filter/Video",
291       "Creates an interlaced video from progressive frames",
292       "David Schleef <ds@schleef.org>");
293 
294   gst_element_class_add_static_pad_template (element_class,
295       &gst_interlace_sink_template);
296   gst_element_class_add_static_pad_template (element_class,
297       &gst_interlace_src_template);
298 
299   element_class->change_state = gst_interlace_change_state;
300 
301   gst_type_mark_as_plugin_api (GST_INTERLACE_PATTERN, 0);
302 }
303 
304 static void
gst_interlace_finalize(GObject * obj)305 gst_interlace_finalize (GObject * obj)
306 {
307   G_OBJECT_CLASS (parent_class)->finalize (obj);
308 }
309 
310 static void
gst_interlace_reset(GstInterlace * interlace)311 gst_interlace_reset (GstInterlace * interlace)
312 {
313   GST_OBJECT_LOCK (interlace);
314   interlace->phase_index = interlace->pattern_offset;
315   GST_OBJECT_UNLOCK (interlace);
316 
317   interlace->timebase = GST_CLOCK_TIME_NONE;
318   interlace->field_index = 0;
319   interlace->passthrough = FALSE;
320   interlace->switch_fields = FALSE;
321   if (interlace->stored_frame) {
322     gst_buffer_unref (interlace->stored_frame);
323     interlace->stored_frame = NULL;
324     interlace->stored_fields = 0;
325   }
326 }
327 
328 static void
gst_interlace_init(GstInterlace * interlace)329 gst_interlace_init (GstInterlace * interlace)
330 {
331   GST_DEBUG ("gst_interlace_init");
332   interlace->sinkpad =
333       gst_pad_new_from_static_template (&gst_interlace_sink_template, "sink");
334   gst_pad_set_chain_function (interlace->sinkpad, gst_interlace_chain);
335   gst_pad_set_event_function (interlace->sinkpad, gst_interlace_sink_event);
336   gst_pad_set_query_function (interlace->sinkpad, gst_interlace_sink_query);
337   gst_element_add_pad (GST_ELEMENT (interlace), interlace->sinkpad);
338 
339   interlace->srcpad =
340       gst_pad_new_from_static_template (&gst_interlace_src_template, "src");
341   gst_pad_set_query_function (interlace->srcpad, gst_interlace_src_query);
342   gst_element_add_pad (GST_ELEMENT (interlace), interlace->srcpad);
343 
344   interlace->top_field_first = FALSE;
345   interlace->allow_rff = FALSE;
346   interlace->pattern = GST_INTERLACE_PATTERN_2_3;
347   interlace->new_pattern = GST_INTERLACE_PATTERN_2_3;
348   interlace->pattern_offset = 0;
349   interlace->src_fps_n = 0;
350   interlace->src_fps_d = 1;
351   gst_interlace_reset (interlace);
352 }
353 
354 typedef struct _PulldownFormat PulldownFormat;
355 struct _PulldownFormat
356 {
357   const gchar *name;
358   /* ratio between outgoing field rate / 2 and incoming frame rate.
359    * I.e., 24p -> 60i is 1.25  */
360   int ratio_n, ratio_d;
361   int n_fields[13];
362 };
363 
364 static const PulldownFormat formats[] = {
365   /* 60p -> 60i or 50p -> 50i */
366   {"1:1", 1, 2, {1}},
367   /* 30p -> 60i or 25p -> 50i */
368   {"2:2", 1, 1, {2}},
369   /* 24p -> 60i telecine */
370   {"2:3", 5, 4, {2, 3,}},
371   {"2:3:3:2", 5, 4, {2, 3, 3, 2,}},
372   /* 24p -> 50i Euro pulldown */
373   {"2-11:3", 25, 24, {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3,}},
374   /* 16p (16000/1001) -> 60i (NTSC 30000/1001) */
375   {"3:4-3", 15, 8, {3, 4, 4, 4,}},
376   /* 16p -> 50i (PAL) */
377   {"3-7:4", 25, 16, {3, 3, 3, 3, 3, 3, 3, 4,}},
378   /* 18p to NTSC 60i */
379   {"3:3:4", 5, 3, {3, 3, 4,}},
380   /* 20p to NTSC 60i */
381   {"3:3", 3, 2, {3, 3,}},
382   /* 27.5 to NTSC 60i */
383   {"3:2-4", 11, 10, {3, 2, 2, 2, 2,}},
384   /* 27.5 to PAL 50i */
385   {"1:2-4", 9, 10, {1, 2, 2, 2, 2,}},
386 };
387 
388 static void
gst_interlace_decorate_buffer_ts(GstInterlace * interlace,GstBuffer * buf,int n_fields)389 gst_interlace_decorate_buffer_ts (GstInterlace * interlace, GstBuffer * buf,
390     int n_fields)
391 {
392   gint src_fps_n, src_fps_d;
393 
394   GST_OBJECT_LOCK (interlace);
395   src_fps_n = interlace->src_fps_n;
396   src_fps_d = interlace->src_fps_d;
397   GST_OBJECT_UNLOCK (interlace);
398 
399   /* field duration = src_fps_d / (2 * src_fps_n) */
400   if (src_fps_n == 0) {
401     /* If we don't know the fps, we can't generate timestamps/durations */
402     GST_BUFFER_DTS (buf) = GST_CLOCK_TIME_NONE;
403     GST_BUFFER_PTS (buf) = GST_CLOCK_TIME_NONE;
404     GST_BUFFER_DURATION (buf) = GST_CLOCK_TIME_NONE;
405   } else {
406     GST_BUFFER_DTS (buf) = interlace->timebase +
407         gst_util_uint64_scale (GST_SECOND,
408         src_fps_d * interlace->fields_since_timebase, src_fps_n * 2);
409     GST_BUFFER_PTS (buf) = GST_BUFFER_DTS (buf);
410     GST_BUFFER_DURATION (buf) =
411         gst_util_uint64_scale (GST_SECOND, src_fps_d * n_fields, src_fps_n * 2);
412   }
413 }
414 
415 static void
gst_interlace_decorate_buffer(GstInterlace * interlace,GstBuffer * buf,int n_fields,gboolean interlaced)416 gst_interlace_decorate_buffer (GstInterlace * interlace, GstBuffer * buf,
417     int n_fields, gboolean interlaced)
418 {
419   GstInterlacePattern pattern;
420 
421   GST_OBJECT_LOCK (interlace);
422   pattern = interlace->pattern;
423   GST_OBJECT_UNLOCK (interlace);
424 
425   gst_interlace_decorate_buffer_ts (interlace, buf, n_fields);
426 
427   if (interlace->field_index == 0) {
428     GST_BUFFER_FLAG_SET (buf, GST_VIDEO_BUFFER_FLAG_TFF);
429   }
430   if (n_fields == 3) {
431     GST_BUFFER_FLAG_SET (buf, GST_VIDEO_BUFFER_FLAG_RFF);
432   }
433   if (n_fields == 1) {
434     GST_BUFFER_FLAG_SET (buf, GST_VIDEO_BUFFER_FLAG_ONEFIELD);
435   }
436   if (pattern > GST_INTERLACE_PATTERN_2_2 && n_fields == 2 && interlaced) {
437     GST_BUFFER_FLAG_SET (buf, GST_VIDEO_BUFFER_FLAG_INTERLACED);
438   }
439 }
440 
441 static const gchar *
interlace_mode_from_pattern(GstInterlace * interlace)442 interlace_mode_from_pattern (GstInterlace * interlace)
443 {
444   GstInterlacePattern pattern;
445 
446   GST_OBJECT_LOCK (interlace);
447   pattern = interlace->pattern;
448   GST_OBJECT_UNLOCK (interlace);
449 
450   if (pattern > GST_INTERLACE_PATTERN_2_2)
451     return "mixed";
452   else
453     return "interleaved";
454 }
455 
456 static GstCaps *
dup_caps_with_alternate(GstCaps * caps)457 dup_caps_with_alternate (GstCaps * caps)
458 {
459   GstCaps *with_alternate;
460   GstCapsFeatures *features;
461 
462   with_alternate = gst_caps_copy (caps);
463   features = gst_caps_features_new (GST_CAPS_FEATURE_FORMAT_INTERLACED, NULL);
464   gst_caps_set_features_simple (with_alternate, features);
465 
466   gst_caps_set_simple (with_alternate, "interlace-mode", G_TYPE_STRING,
467       "alternate", NULL);
468 
469   return with_alternate;
470 }
471 
472 static gboolean
gst_interlace_setcaps(GstInterlace * interlace,GstCaps * caps)473 gst_interlace_setcaps (GstInterlace * interlace, GstCaps * caps)
474 {
475   gboolean ret;
476   GstVideoInfo info, out_info;
477   GstCaps *othercaps, *src_peer_caps;
478   const PulldownFormat *pdformat;
479   gboolean top_field_first, alternate;
480   int i;
481   int src_fps_n, src_fps_d;
482   GstInterlacePattern pattern;
483 
484   if (!gst_video_info_from_caps (&info, caps))
485     goto caps_error;
486 
487   GST_OBJECT_LOCK (interlace);
488   interlace->pattern = interlace->new_pattern;
489   pattern = interlace->pattern;
490   top_field_first = interlace->top_field_first;
491   GST_OBJECT_UNLOCK (interlace);
492 
493   /* Check if downstream prefers alternate mode */
494   othercaps = gst_caps_copy (caps);
495   gst_caps_set_simple (othercaps, "interlace-mode", G_TYPE_STRING,
496       interlace_mode_from_pattern (interlace), NULL);
497   gst_caps_append (othercaps, dup_caps_with_alternate (othercaps));
498   if (pattern == GST_INTERLACE_PATTERN_2_2) {
499     for (i = 0; i < gst_caps_get_size (othercaps); ++i) {
500       GstStructure *s;
501 
502       s = gst_caps_get_structure (othercaps, i);
503       gst_structure_remove_field (s, "field-order");
504     }
505   } else if (pattern == GST_INTERLACE_PATTERN_1_1 &&
506       GST_VIDEO_INFO_INTERLACE_MODE (&info) ==
507       GST_VIDEO_INTERLACE_MODE_PROGRESSIVE) {
508     /* interlaced will do passthrough, mixed will fail later in the
509      * negotiation */
510     othercaps = gst_interlace_caps_double_framerate (othercaps, TRUE, FALSE);
511   } else if (pattern > GST_INTERLACE_PATTERN_2_2) {
512     GST_FIXME_OBJECT (interlace,
513         "Add calculations for telecine framerate conversions");
514     for (i = 0; i < gst_caps_get_size (othercaps); ++i) {
515       GstStructure *s = gst_caps_get_structure (othercaps, i);
516 
517       gst_structure_remove_field (s, "framerate");
518     }
519   }
520   src_peer_caps = gst_pad_peer_query_caps (interlace->srcpad, othercaps);
521   gst_caps_unref (othercaps);
522   othercaps = gst_caps_fixate (src_peer_caps);
523   if (gst_caps_is_empty (othercaps)) {
524     gst_caps_unref (othercaps);
525     goto caps_error;
526   }
527   if (!gst_video_info_from_caps (&out_info, othercaps)) {
528     gst_caps_unref (othercaps);
529     goto caps_error;
530   }
531 
532   alternate =
533       GST_VIDEO_INFO_INTERLACE_MODE (&out_info) ==
534       GST_VIDEO_INTERLACE_MODE_ALTERNATE;
535 
536   pdformat = &formats[pattern];
537 
538   src_fps_n = info.fps_n * pdformat->ratio_n;
539   src_fps_d = info.fps_d * pdformat->ratio_d;
540 
541   GST_OBJECT_LOCK (interlace);
542   interlace->phase_index = interlace->pattern_offset;
543   interlace->src_fps_n = src_fps_n;
544   interlace->src_fps_d = src_fps_d;
545   GST_OBJECT_UNLOCK (interlace);
546 
547   GST_DEBUG_OBJECT (interlace, "new framerate %d/%d", src_fps_n, src_fps_d);
548 
549   if (alternate) {
550     GST_DEBUG_OBJECT (interlace,
551         "producing alternate stream as requested downstream");
552   }
553 
554   interlace->switch_fields = FALSE;
555   if (gst_caps_can_intersect (caps, othercaps) &&
556       pattern <= GST_INTERLACE_PATTERN_2_2 &&
557       GST_VIDEO_INFO_INTERLACE_MODE (&info) != GST_VIDEO_INTERLACE_MODE_MIXED) {
558     /* FIXME: field-order is optional in the caps. This means that, if we're
559      * in a non-telecine mode and we have TFF upstream and
560      * top-field-first=FALSE in interlace (or the other way around), AND
561      * field-order isn't mentioned in the caps, we will do passthrough here
562      * and end up outptuting wrong data. Must detect missing field-order info
563      * and not do passthrough in that case, but instead check the
564      * GstVideoBufferFlags at the switch_fields check */
565     interlace->passthrough = TRUE;
566   } else {
567     if (GST_VIDEO_INFO_IS_INTERLACED (&info)) {
568       if (pattern == GST_INTERLACE_PATTERN_2_2) {
569         /* There is a chance we'd have to switch fields when in fact doing
570          * passthrough - see FIXME comment above, basically it would
571          * auto-negotiate to passthrough (because field-order is missing from
572          * the caps) */
573         GstCaps *clonedcaps = gst_caps_copy (othercaps);
574         for (i = 0; i < gst_caps_get_size (clonedcaps); ++i) {
575           GstStructure *s = gst_caps_get_structure (clonedcaps, i);
576 
577           gst_structure_remove_field (s, "field-order");
578         }
579         if (gst_caps_can_intersect (caps, clonedcaps)) {
580           interlace->switch_fields = TRUE;
581           gst_caps_unref (clonedcaps);
582         } else {
583           gst_caps_unref (clonedcaps);
584           GST_ERROR_OBJECT (interlace,
585               "Caps %" GST_PTR_FORMAT " not compatible with %" GST_PTR_FORMAT,
586               caps, othercaps);
587           gst_caps_unref (othercaps);
588           goto caps_error;
589         }
590       } else {
591         GST_ERROR_OBJECT (interlace,
592             "Caps %" GST_PTR_FORMAT " not compatible with %" GST_PTR_FORMAT,
593             caps, othercaps);
594         gst_caps_unref (othercaps);
595         goto caps_error;
596       }
597     }
598     interlace->passthrough = FALSE;
599     gst_caps_set_simple (othercaps, "framerate", GST_TYPE_FRACTION, src_fps_n,
600         src_fps_d, NULL);
601     if (pattern <= GST_INTERLACE_PATTERN_2_2 || alternate) {
602       gst_caps_set_simple (othercaps, "field-order", G_TYPE_STRING,
603           top_field_first ? "top-field-first" : "bottom-field-first", NULL);
604     }
605     /* outcaps changed, regenerate out_info */
606     gst_video_info_from_caps (&out_info, othercaps);
607   }
608 
609   GST_DEBUG_OBJECT (interlace->sinkpad, "set caps %" GST_PTR_FORMAT, caps);
610   GST_DEBUG_OBJECT (interlace->srcpad, "set caps %" GST_PTR_FORMAT, othercaps);
611 
612   ret = gst_pad_set_caps (interlace->srcpad, othercaps);
613   gst_caps_unref (othercaps);
614 
615   interlace->info = info;
616   interlace->out_info = out_info;
617 
618   return ret;
619 
620 caps_error:
621   {
622     GST_DEBUG_OBJECT (interlace, "error parsing caps");
623     return FALSE;
624   }
625 }
626 
627 static gboolean
gst_interlace_sink_event(GstPad * pad,GstObject * parent,GstEvent * event)628 gst_interlace_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
629 {
630   gboolean ret;
631   GstInterlace *interlace;
632 
633   interlace = GST_INTERLACE (parent);
634 
635   switch (GST_EVENT_TYPE (event)) {
636     case GST_EVENT_FLUSH_START:
637       GST_DEBUG_OBJECT (interlace, "handling FLUSH_START");
638       ret = gst_pad_push_event (interlace->srcpad, event);
639       break;
640     case GST_EVENT_FLUSH_STOP:
641       GST_DEBUG_OBJECT (interlace, "handling FLUSH_STOP");
642       gst_interlace_reset (interlace);
643       ret = gst_pad_push_event (interlace->srcpad, event);
644       break;
645     case GST_EVENT_EOS:
646 #if 0
647       /* FIXME revive this when we output ONEFIELD and RFF buffers */
648     {
649       gint num_fields;
650       const PulldownFormat *format = &formats[interlace->pattern];
651 
652       num_fields =
653           format->n_fields[interlace->phase_index] -
654           interlace->stored_fields_pushed;
655       interlace->stored_fields_pushed = 0;
656 
657       /* on EOS we want to push as many sane frames as are left */
658       while (num_fields > 1) {
659         GstBuffer *output_buffer;
660 
661         /* make metadata writable before editing it */
662         interlace->stored_frame =
663             gst_buffer_make_metadata_writable (interlace->stored_frame);
664         num_fields -= 2;
665 
666         gst_interlace_decorate_buffer (interlace, interlace->stored_frame,
667             n_fields, FALSE);
668 
669         /* ref output_buffer/stored frame because we want to keep it for now
670          * and pushing gives away a ref */
671         output_buffer = gst_buffer_ref (interlace->stored_frame);
672         if (gst_pad_push (interlace->srcpad, output_buffer)) {
673           GST_DEBUG_OBJECT (interlace, "Failed to push buffer %p",
674               output_buffer);
675           return FALSE;
676         }
677         output_buffer = NULL;
678 
679         if (num_fields <= 1) {
680           gst_buffer_unref (interlace->stored_frame);
681           interlace->stored_frame = NULL;
682           break;
683         }
684       }
685 
686       /* increment the phase index */
687       interlace->phase_index++;
688       if (!format->n_fields[interlace->phase_index]) {
689         interlace->phase_index = 0;
690       }
691     }
692 #endif
693 
694       if (interlace->stored_frame) {
695         gst_buffer_unref (interlace->stored_frame);
696         interlace->stored_frame = NULL;
697         interlace->stored_fields = 0;
698       }
699       ret = gst_pad_push_event (interlace->srcpad, event);
700       break;
701     case GST_EVENT_CAPS:
702     {
703       GstCaps *caps;
704 
705       gst_event_parse_caps (event, &caps);
706       ret = gst_interlace_setcaps (interlace, caps);
707       gst_event_unref (event);
708       break;
709     }
710     default:
711       ret = gst_pad_push_event (interlace->srcpad, event);
712       break;
713   }
714 
715   return ret;
716 }
717 
718 static gboolean
gst_interlace_fraction_double(gint * n_out,gint * d_out,gboolean half)719 gst_interlace_fraction_double (gint * n_out, gint * d_out, gboolean half)
720 {
721   gint n, d, gcd;
722 
723   n = *n_out;
724   d = *d_out;
725 
726   if (d == 0)
727     return FALSE;
728 
729   if (n == 0)
730     return TRUE;
731 
732   gcd = gst_util_greatest_common_divisor (n, d);
733   n /= gcd;
734   d /= gcd;
735 
736   if (half) {
737     if (G_MAXINT / 2 >= ABS (d)) {
738       d *= 2;
739     } else if (n >= 2 && n != G_MAXINT) {
740       n /= 2;
741     } else {
742       d = G_MAXINT;
743     }
744   } else {
745     if (G_MAXINT / 2 >= ABS (n)) {
746       n *= 2;
747     } else if (d >= 2 && d != G_MAXINT) {
748       d /= 2;
749     } else {
750       n = G_MAXINT;
751     }
752   }
753 
754   *n_out = n;
755   *d_out = d;
756 
757   return TRUE;
758 }
759 
760 static GstCaps *
gst_interlace_caps_double_framerate(GstCaps * caps,gboolean half,gboolean skip_progressive)761 gst_interlace_caps_double_framerate (GstCaps * caps, gboolean half,
762     gboolean skip_progressive)
763 {
764   guint len;
765 
766   for (len = gst_caps_get_size (caps); len > 0; len--) {
767     GstStructure *s = gst_caps_get_structure (caps, len - 1);
768     const GValue *val;
769     const gchar *interlace_mode;
770 
771     val = gst_structure_get_value (s, "framerate");
772     if (!val)
773       continue;
774 
775     interlace_mode = gst_structure_get_string (s, "interlace-mode");
776     /* Do not double the framerate for interlaced - we will either passthrough
777      * or fail to negotiate */
778     if (skip_progressive && (interlace_mode
779             && g_strcmp0 (interlace_mode, "progressive") != 0))
780       continue;
781 
782     if (G_VALUE_TYPE (val) == GST_TYPE_FRACTION) {
783       gint n, d;
784 
785       n = gst_value_get_fraction_numerator (val);
786       d = gst_value_get_fraction_denominator (val);
787 
788       if (!gst_interlace_fraction_double (&n, &d, half)) {
789         gst_caps_remove_structure (caps, len - 1);
790         continue;
791       }
792 
793       gst_structure_set (s, "framerate", GST_TYPE_FRACTION, n, d, NULL);
794     } else if (G_VALUE_TYPE (val) == GST_TYPE_FRACTION_RANGE) {
795       const GValue *min, *max;
796       GValue nrange = { 0, }, nmin = {
797       0,}, nmax = {
798       0,};
799       gint n, d;
800 
801       g_value_init (&nrange, GST_TYPE_FRACTION_RANGE);
802       g_value_init (&nmin, GST_TYPE_FRACTION);
803       g_value_init (&nmax, GST_TYPE_FRACTION);
804 
805       min = gst_value_get_fraction_range_min (val);
806       max = gst_value_get_fraction_range_max (val);
807 
808       n = gst_value_get_fraction_numerator (min);
809       d = gst_value_get_fraction_denominator (min);
810 
811       if (!gst_interlace_fraction_double (&n, &d, half)) {
812         g_value_unset (&nrange);
813         g_value_unset (&nmax);
814         g_value_unset (&nmin);
815         gst_caps_remove_structure (caps, len - 1);
816         continue;
817       }
818 
819       gst_value_set_fraction (&nmin, n, d);
820 
821       n = gst_value_get_fraction_numerator (max);
822       d = gst_value_get_fraction_denominator (max);
823 
824       if (!gst_interlace_fraction_double (&n, &d, half)) {
825         g_value_unset (&nrange);
826         g_value_unset (&nmax);
827         g_value_unset (&nmin);
828         gst_caps_remove_structure (caps, len - 1);
829         continue;
830       }
831 
832       gst_value_set_fraction (&nmax, n, d);
833       gst_value_set_fraction_range (&nrange, &nmin, &nmax);
834 
835       gst_structure_take_value (s, "framerate", &nrange);
836 
837       g_value_unset (&nmin);
838       g_value_unset (&nmax);
839     } else if (G_VALUE_TYPE (val) == GST_TYPE_LIST) {
840       const GValue *lval;
841       GValue nlist = { 0, };
842       GValue nval = { 0, };
843       gint i;
844 
845       g_value_init (&nlist, GST_TYPE_LIST);
846       for (i = gst_value_list_get_size (val); i > 0; i--) {
847         gint n, d;
848 
849         lval = gst_value_list_get_value (val, i - 1);
850 
851         if (G_VALUE_TYPE (lval) != GST_TYPE_FRACTION)
852           continue;
853 
854         n = gst_value_get_fraction_numerator (lval);
855         d = gst_value_get_fraction_denominator (lval);
856 
857         /* Double/Half the framerate but if this fails simply
858          * skip this value from the list */
859         if (!gst_interlace_fraction_double (&n, &d, half)) {
860           continue;
861         }
862 
863         g_value_init (&nval, GST_TYPE_FRACTION);
864 
865         gst_value_set_fraction (&nval, n, d);
866         gst_value_list_append_and_take_value (&nlist, &nval);
867       }
868       gst_structure_take_value (s, "framerate", &nlist);
869     }
870   }
871 
872   return caps;
873 }
874 
875 static GstCaps *
gst_interlace_getcaps(GstPad * pad,GstInterlace * interlace,GstCaps * filter)876 gst_interlace_getcaps (GstPad * pad, GstInterlace * interlace, GstCaps * filter)
877 {
878   GstPad *otherpad;
879   GstCaps *othercaps, *tcaps;
880   GstCaps *icaps;
881   GstCaps *clean_filter = NULL;
882   const char *mode;
883   guint i;
884   gint pattern;
885   gboolean top_field_first;
886 
887   otherpad =
888       (pad == interlace->srcpad) ? interlace->sinkpad : interlace->srcpad;
889 
890   GST_OBJECT_LOCK (interlace);
891   pattern = interlace->new_pattern;
892   top_field_first = interlace->top_field_first;
893   GST_OBJECT_UNLOCK (interlace);
894 
895   GST_DEBUG_OBJECT (pad, "Querying caps with filter %" GST_PTR_FORMAT, filter);
896 
897   if (filter != NULL) {
898     clean_filter = gst_caps_copy (filter);
899     if (pattern == GST_INTERLACE_PATTERN_1_1) {
900       clean_filter =
901           gst_interlace_caps_double_framerate (clean_filter,
902           (pad == interlace->sinkpad), TRUE);
903     } else if (pattern != GST_INTERLACE_PATTERN_2_2) {
904       GST_FIXME_OBJECT (interlace,
905           "Add calculations for telecine framerate conversions");
906       for (i = 0; i < gst_caps_get_size (clean_filter); ++i) {
907         GstStructure *s = gst_caps_get_structure (clean_filter, i);
908 
909         gst_structure_remove_field (s, "framerate");
910       }
911     }
912 
913     if (pad == interlace->sinkpad) {
914       /* @filter may contain the different formats supported upstream.
915        * Those will be used to filter the src pad caps as this element
916        * is not supposed to do any video format conversion.
917        * Add a variant of the filter with the Interlaced feature as we want
918        * to be able to negotiate it if needed.
919        */
920       gst_caps_append (clean_filter, dup_caps_with_alternate (clean_filter));
921     }
922 
923     for (i = 0; i < gst_caps_get_size (clean_filter); ++i) {
924       GstStructure *s;
925 
926       s = gst_caps_get_structure (clean_filter, i);
927       gst_structure_remove_field (s, "interlace-mode");
928       if (pattern == GST_INTERLACE_PATTERN_2_2 && pad == interlace->sinkpad) {
929         gst_structure_remove_field (s, "field-order");
930       }
931     }
932   }
933 
934   GST_DEBUG_OBJECT (pad, "Querying peer with filter %" GST_PTR_FORMAT,
935       clean_filter);
936   tcaps = gst_pad_get_pad_template_caps (otherpad);
937   othercaps = gst_pad_peer_query_caps (otherpad, clean_filter);
938   othercaps = gst_caps_make_writable (othercaps);
939   GST_DEBUG_OBJECT (pad, "Other caps %" GST_PTR_FORMAT, othercaps);
940   if (othercaps) {
941     if (pattern == GST_INTERLACE_PATTERN_2_2) {
942       for (i = 0; i < gst_caps_get_size (othercaps); ++i) {
943         GstStructure *s = gst_caps_get_structure (othercaps, i);
944 
945         if (pad == interlace->srcpad) {
946           gst_structure_set (s, "field-order", G_TYPE_STRING,
947               top_field_first ? "top-field-first" : "bottom-field-first", NULL);
948         } else {
949           gst_structure_remove_field (s, "field-order");
950         }
951       }
952     }
953     icaps = gst_caps_intersect (othercaps, tcaps);
954     gst_caps_unref (othercaps);
955     gst_caps_unref (tcaps);
956   } else {
957     icaps = tcaps;
958   }
959 
960   if (clean_filter) {
961     othercaps = gst_caps_intersect (icaps, clean_filter);
962     gst_caps_unref (icaps);
963     icaps = othercaps;
964   }
965 
966   icaps = gst_caps_make_writable (icaps);
967   mode = interlace_mode_from_pattern (interlace);
968 
969   if (pad == interlace->srcpad) {
970     /* Set interlace-mode to what the element will produce, so either
971      * mixed/interleaved or alternate if the caps feature is present. */
972     gst_caps_set_simple (icaps, "interlace-mode", G_TYPE_STRING, mode, NULL);
973     icaps = gst_caps_merge (icaps, dup_caps_with_alternate (icaps));
974   } else {
975     GstCaps *interlaced, *alternate;
976 
977     /* Sink pad is supposed to receive a progressive stream so remove the
978      * Interlaced feature and set interlace-mode=progressive */
979     for (i = 0; i < gst_caps_get_size (icaps); ++i) {
980       GstCapsFeatures *features;
981       GstStructure *s = gst_caps_get_structure (icaps, i);
982 
983       features = gst_caps_get_features (icaps, i);
984       gst_caps_features_remove (features, GST_CAPS_FEATURE_FORMAT_INTERLACED);
985 
986       /* Drop field-order field for sinkpad */
987       gst_structure_remove_field (s, "field-order");
988     }
989 
990     gst_caps_set_simple (icaps, "interlace-mode", G_TYPE_STRING, "progressive",
991         NULL);
992 
993     /* Now add variants of the same caps with the interlace-mode and Interlaced
994      * caps so we can operate in passthrough if needed. */
995     interlaced = gst_caps_copy (icaps);
996     gst_caps_set_simple (interlaced, "interlace-mode", G_TYPE_STRING, mode,
997         NULL);
998     alternate = dup_caps_with_alternate (icaps);
999 
1000     icaps = gst_caps_merge (icaps, interlaced);
1001     icaps = gst_caps_merge (icaps, alternate);
1002   }
1003 
1004   /* Drop framerate for sinkpad */
1005   if (pad == interlace->sinkpad) {
1006     for (i = 0; i < gst_caps_get_size (icaps); ++i) {
1007       GstStructure *s = gst_caps_get_structure (icaps, i);
1008 
1009       gst_structure_remove_field (s, "framerate");
1010     }
1011   } else {
1012     if (pattern == GST_INTERLACE_PATTERN_1_1) {
1013       icaps = gst_interlace_caps_double_framerate (icaps, TRUE, FALSE);
1014     } else if (pattern != GST_INTERLACE_PATTERN_2_2) {
1015       GST_FIXME_OBJECT (interlace,
1016           "Add calculations for telecine framerate conversions");
1017       for (i = 0; i < gst_caps_get_size (icaps); ++i) {
1018         GstStructure *s = gst_caps_get_structure (icaps, i);
1019 
1020         gst_structure_remove_field (s, "framerate");
1021       }
1022     }
1023   }
1024 
1025   if (clean_filter)
1026     gst_caps_unref (clean_filter);
1027 
1028   GST_DEBUG_OBJECT (pad, "caps: %" GST_PTR_FORMAT, icaps);
1029   return icaps;
1030 }
1031 
1032 static gboolean
gst_interlace_sink_query(GstPad * pad,GstObject * parent,GstQuery * query)1033 gst_interlace_sink_query (GstPad * pad, GstObject * parent, GstQuery * query)
1034 {
1035   gboolean ret;
1036   GstInterlace *interlace;
1037 
1038   interlace = GST_INTERLACE (parent);
1039 
1040   switch (GST_QUERY_TYPE (query)) {
1041     case GST_QUERY_CAPS:
1042     {
1043       GstCaps *filter, *caps;
1044 
1045       gst_query_parse_caps (query, &filter);
1046       caps = gst_interlace_getcaps (pad, interlace, filter);
1047       gst_query_set_caps_result (query, caps);
1048       gst_caps_unref (caps);
1049       ret = TRUE;
1050       break;
1051     }
1052     default:
1053       ret = gst_pad_query_default (pad, parent, query);
1054       break;
1055   }
1056   return ret;
1057 }
1058 
1059 static gboolean
gst_interlace_src_query(GstPad * pad,GstObject * parent,GstQuery * query)1060 gst_interlace_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
1061 {
1062   gboolean ret;
1063   GstInterlace *interlace;
1064 
1065   interlace = GST_INTERLACE (parent);
1066 
1067   switch (GST_QUERY_TYPE (query)) {
1068     case GST_QUERY_CAPS:
1069     {
1070       GstCaps *filter, *caps;
1071 
1072       gst_query_parse_caps (query, &filter);
1073       caps = gst_interlace_getcaps (pad, interlace, filter);
1074       gst_query_set_caps_result (query, caps);
1075       gst_caps_unref (caps);
1076       ret = TRUE;
1077       break;
1078     }
1079     default:
1080       ret = gst_pad_query_default (pad, parent, query);
1081       break;
1082   }
1083   return ret;
1084 }
1085 
1086 static void
copy_fields(GstInterlace * interlace,GstBuffer * dest,GstBuffer * src,int field_index)1087 copy_fields (GstInterlace * interlace, GstBuffer * dest, GstBuffer * src,
1088     int field_index)
1089 {
1090   GstVideoInfo *in_info = &interlace->info;
1091   GstVideoInfo *out_info = &interlace->out_info;
1092   gint i, j, n_planes;
1093   guint8 *d, *s;
1094   GstVideoFrame dframe, sframe;
1095 
1096   if (!gst_video_frame_map (&dframe, out_info, dest, GST_MAP_WRITE))
1097     goto dest_map_failed;
1098 
1099   if (!gst_video_frame_map (&sframe, in_info, src, GST_MAP_READ))
1100     goto src_map_failed;
1101 
1102   n_planes = GST_VIDEO_FRAME_N_PLANES (&dframe);
1103 
1104   for (i = 0; i < n_planes; i++) {
1105     gint cheight, cwidth;
1106     gint ss, ds;
1107 
1108     d = GST_VIDEO_FRAME_PLANE_DATA (&dframe, i);
1109     s = GST_VIDEO_FRAME_PLANE_DATA (&sframe, i);
1110 
1111     ds = GST_VIDEO_FRAME_PLANE_STRIDE (&dframe, i);
1112     ss = GST_VIDEO_FRAME_PLANE_STRIDE (&sframe, i);
1113 
1114     d += field_index * ds;
1115     if (!interlace->switch_fields) {
1116       s += field_index * ss;
1117     } else {
1118       s += (field_index ^ 1) * ss;
1119     }
1120 
1121     cheight = GST_VIDEO_FRAME_COMP_HEIGHT (&dframe, i);
1122     cwidth = MIN (ABS (ss), ABS (ds));
1123 
1124     for (j = field_index; j < cheight; j += 2) {
1125       memcpy (d, s, cwidth);
1126       d += ds * 2;
1127       s += ss * 2;
1128     }
1129   }
1130 
1131   gst_video_frame_unmap (&dframe);
1132   gst_video_frame_unmap (&sframe);
1133   return;
1134 
1135 dest_map_failed:
1136   {
1137     GST_ERROR_OBJECT (interlace, "failed to map dest");
1138     return;
1139   }
1140 src_map_failed:
1141   {
1142     GST_ERROR_OBJECT (interlace, "failed to map src");
1143     gst_video_frame_unmap (&dframe);
1144     return;
1145   }
1146 }
1147 
1148 static GstBuffer *
copy_field(GstInterlace * interlace,GstBuffer * src,int field_index)1149 copy_field (GstInterlace * interlace, GstBuffer * src, int field_index)
1150 {
1151   gint i, j, n_planes;
1152   GstVideoFrame dframe, sframe;
1153   GstBuffer *dest;
1154 
1155   dest =
1156       gst_buffer_new_allocate (NULL, GST_VIDEO_INFO_SIZE (&interlace->out_info),
1157       NULL);
1158 
1159   if (!gst_video_frame_map (&dframe, &interlace->out_info, dest, GST_MAP_WRITE))
1160     goto dest_map_failed;
1161 
1162   if (!gst_video_frame_map (&sframe, &interlace->info, src, GST_MAP_READ))
1163     goto src_map_failed;
1164 
1165   n_planes = GST_VIDEO_FRAME_N_PLANES (&dframe);
1166 
1167   for (i = 0; i < n_planes; i++) {
1168     guint8 *d, *s;
1169     gint cheight, cwidth;
1170     gint ss, ds;
1171 
1172     d = GST_VIDEO_FRAME_PLANE_DATA (&dframe, i);
1173     s = GST_VIDEO_FRAME_PLANE_DATA (&sframe, i);
1174 
1175     ds = GST_VIDEO_FRAME_PLANE_STRIDE (&dframe, i);
1176     ss = GST_VIDEO_FRAME_PLANE_STRIDE (&sframe, i);
1177 
1178     cheight = GST_VIDEO_FRAME_COMP_HEIGHT (&sframe, i);
1179     cwidth = MIN (ABS (ss), ABS (ds));
1180 
1181     for (j = field_index; j < cheight; j += 2) {
1182       memcpy (d, s, cwidth);
1183       d += ds;
1184       s += ss * 2;
1185     }
1186   }
1187 
1188   gst_video_frame_unmap (&dframe);
1189   gst_video_frame_unmap (&sframe);
1190   return dest;
1191 dest_map_failed:
1192   {
1193     GST_ELEMENT_ERROR (interlace, CORE, FAILED, ("Failed to write map buffer"),
1194         ("Failed to map dest buffer for field %d", field_index));
1195     gst_buffer_unref (dest);
1196     return NULL;
1197   }
1198 src_map_failed:
1199   {
1200     GST_ELEMENT_ERROR (interlace, CORE, FAILED, ("Failed to read map buffer"),
1201         ("Failed to map source buffer for field %d", field_index));
1202     gst_buffer_unref (dest);
1203     gst_video_frame_unmap (&dframe);
1204     return NULL;
1205   }
1206 }
1207 
1208 static GstFlowReturn
gst_interlace_push_buffer(GstInterlace * interlace,GstBuffer * buffer)1209 gst_interlace_push_buffer (GstInterlace * interlace, GstBuffer * buffer)
1210 {
1211   GST_DEBUG_OBJECT (interlace, "output timestamp %" GST_TIME_FORMAT
1212       " duration %" GST_TIME_FORMAT " flags %04x %s %s %s",
1213       GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
1214       GST_TIME_ARGS (GST_BUFFER_DURATION (buffer)),
1215       GST_BUFFER_FLAGS (buffer),
1216       (GST_BUFFER_FLAGS (buffer) & GST_VIDEO_BUFFER_FLAG_TFF) ? "tff" :
1217       "",
1218       (GST_BUFFER_FLAGS (buffer) & GST_VIDEO_BUFFER_FLAG_RFF) ? "rff" :
1219       "",
1220       (GST_BUFFER_FLAGS (buffer) & GST_VIDEO_BUFFER_FLAG_ONEFIELD) ?
1221       "onefield" : "");
1222 
1223   return gst_pad_push (interlace->srcpad, buffer);
1224 }
1225 
1226 static GstFlowReturn
gst_interlace_chain(GstPad * pad,GstObject * parent,GstBuffer * buffer)1227 gst_interlace_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
1228 {
1229   GstInterlace *interlace = GST_INTERLACE (parent);
1230   GstFlowReturn ret = GST_FLOW_OK;
1231   gint num_fields = 0;
1232   guint current_fields, pattern_offset;
1233   const PulldownFormat *format;
1234   GstClockTime timestamp;
1235   gboolean allow_rff, top_field_first, alternate;
1236 
1237   timestamp = GST_BUFFER_TIMESTAMP (buffer);
1238 
1239   GST_DEBUG ("Received buffer at %" GST_TIME_FORMAT, GST_TIME_ARGS (timestamp));
1240 
1241   GST_DEBUG ("duration %" GST_TIME_FORMAT " flags %04x %s %s %s",
1242       GST_TIME_ARGS (GST_BUFFER_DURATION (buffer)),
1243       GST_BUFFER_FLAGS (buffer),
1244       (GST_BUFFER_FLAGS (buffer) & GST_VIDEO_BUFFER_FLAG_TFF) ? "tff" : "",
1245       (GST_BUFFER_FLAGS (buffer) & GST_VIDEO_BUFFER_FLAG_RFF) ? "rff" : "",
1246       (GST_BUFFER_FLAGS (buffer) & GST_VIDEO_BUFFER_FLAG_ONEFIELD) ? "onefield"
1247       : "");
1248 
1249   if (interlace->passthrough) {
1250     return gst_pad_push (interlace->srcpad, buffer);
1251   }
1252 
1253   GST_OBJECT_LOCK (interlace);
1254   format = &formats[interlace->pattern];
1255   allow_rff = interlace->allow_rff;
1256   pattern_offset = interlace->pattern_offset;
1257   top_field_first = interlace->top_field_first;
1258   GST_OBJECT_UNLOCK (interlace);
1259 
1260   if (GST_BUFFER_FLAGS (buffer) & GST_BUFFER_FLAG_DISCONT) {
1261     GST_DEBUG ("discont");
1262 
1263     if (interlace->stored_frame) {
1264       gst_buffer_unref (interlace->stored_frame);
1265       interlace->stored_frame = NULL;
1266       interlace->stored_fields = 0;
1267     }
1268 
1269     if (top_field_first) {
1270       interlace->field_index = 0;
1271     } else {
1272       interlace->field_index = 1;
1273     }
1274   }
1275 
1276   if (interlace->timebase == GST_CLOCK_TIME_NONE) {
1277     /* get the initial ts */
1278     interlace->timebase = timestamp;
1279   }
1280 
1281   if (interlace->stored_fields == 0
1282       && interlace->phase_index == pattern_offset
1283       && GST_CLOCK_TIME_IS_VALID (timestamp)) {
1284     interlace->timebase = timestamp;
1285     interlace->fields_since_timebase = 0;
1286   }
1287 
1288   current_fields = format->n_fields[interlace->phase_index];
1289   /* increment the phase index */
1290   interlace->phase_index++;
1291   g_assert (interlace->phase_index < G_N_ELEMENTS (format->n_fields));
1292   if (!format->n_fields[interlace->phase_index]) {
1293     interlace->phase_index = 0;
1294   }
1295   if (interlace->switch_fields && !interlace->stored_frame) {
1296     /* When switching fields, we want to skip the very first field of the very
1297      * first frame, then take one field from the stored frame and one from the
1298      * current one. This happens in the code when we do not have enough fields
1299      * available on current_fields, so we decrement the number, which is what
1300      * would happen if we had used one field. This way, the current frame
1301      * will be stored and then its other field will be used the next time the
1302      * chain function is called */
1303     current_fields--;
1304   }
1305 
1306   GST_DEBUG ("incoming buffer assigned %d fields", current_fields);
1307 
1308   alternate =
1309       GST_VIDEO_INFO_INTERLACE_MODE (&interlace->out_info) ==
1310       GST_VIDEO_INTERLACE_MODE_ALTERNATE;
1311 
1312   num_fields = interlace->stored_fields + current_fields;
1313   while (num_fields >= 2) {
1314     GstBuffer *output_buffer, *output_buffer2 = NULL;
1315     guint n_output_fields;
1316     gboolean interlaced = FALSE;
1317     GstVideoInfo *in_info = &interlace->info;
1318     GstVideoInfo *out_info = &interlace->out_info;
1319 
1320     GST_DEBUG ("have %d fields, %d current, %d stored",
1321         num_fields, current_fields, interlace->stored_fields);
1322 
1323     if (interlace->stored_fields > 0) {
1324       GST_DEBUG ("1 field from stored, 1 from current");
1325 
1326       if (alternate) {
1327         /* take the first field from the stored frame */
1328         output_buffer = copy_field (interlace, interlace->stored_frame,
1329             interlace->field_index);
1330         if (!output_buffer)
1331           return GST_FLOW_ERROR;
1332         /* take the second field from the incoming buffer */
1333         output_buffer2 = copy_field (interlace, buffer,
1334             interlace->field_index ^ 1);
1335         if (!output_buffer2)
1336           return GST_FLOW_ERROR;
1337       } else {
1338         output_buffer =
1339             gst_buffer_new_and_alloc (GST_VIDEO_INFO_SIZE (out_info));
1340         /* take the first field from the stored frame */
1341         copy_fields (interlace, output_buffer, interlace->stored_frame,
1342             interlace->field_index);
1343         /* take the second field from the incoming buffer */
1344         copy_fields (interlace, output_buffer, buffer,
1345             interlace->field_index ^ 1);
1346       }
1347 
1348       interlace->stored_fields--;
1349       current_fields--;
1350       n_output_fields = 2;
1351       interlaced = TRUE;
1352     } else {
1353       if (alternate) {
1354         output_buffer = copy_field (interlace, buffer, interlace->field_index);
1355         if (!output_buffer)
1356           return GST_FLOW_ERROR;
1357         output_buffer2 =
1358             copy_field (interlace, buffer, interlace->field_index ^ 1);
1359         if (!output_buffer2)
1360           return GST_FLOW_ERROR;
1361       } else {
1362         GstVideoFrame dframe, sframe;
1363 
1364         output_buffer =
1365             gst_buffer_new_and_alloc (GST_VIDEO_INFO_SIZE (out_info));
1366 
1367         if (!gst_video_frame_map (&dframe,
1368                 out_info, output_buffer, GST_MAP_WRITE)) {
1369           GST_ELEMENT_ERROR (interlace, CORE, FAILED,
1370               ("Failed to write map buffer"), ("Failed to map output buffer"));
1371           gst_buffer_unref (output_buffer);
1372           gst_buffer_unref (buffer);
1373           return GST_FLOW_ERROR;
1374         }
1375 
1376         if (!gst_video_frame_map (&sframe, in_info, buffer, GST_MAP_READ)) {
1377           GST_ELEMENT_ERROR (interlace, CORE, FAILED,
1378               ("Failed to read map buffer"), ("Failed to map input buffer"));
1379           gst_video_frame_unmap (&dframe);
1380           gst_buffer_unref (output_buffer);
1381           gst_buffer_unref (buffer);
1382           return GST_FLOW_ERROR;
1383         }
1384 
1385         gst_video_frame_copy (&dframe, &sframe);
1386         gst_video_frame_unmap (&dframe);
1387         gst_video_frame_unmap (&sframe);
1388       }
1389 
1390       if (num_fields >= 3 && allow_rff) {
1391         GST_DEBUG ("3 fields from current");
1392         /* take both fields from incoming buffer */
1393         current_fields -= 3;
1394         n_output_fields = 3;
1395       } else {
1396         GST_DEBUG ("2 fields from current");
1397         /* take both buffers from incoming buffer */
1398         current_fields -= 2;
1399         n_output_fields = 2;
1400       }
1401     }
1402     num_fields -= n_output_fields;
1403 
1404     if (!alternate) {
1405       g_assert (!output_buffer2);
1406       gst_interlace_decorate_buffer (interlace, output_buffer, n_output_fields,
1407           interlaced);
1408     } else {
1409       g_assert (output_buffer2);
1410       gst_interlace_decorate_buffer_ts (interlace, output_buffer,
1411           n_output_fields);
1412 
1413       /* Both fields share the same ts */
1414       GST_BUFFER_PTS (output_buffer2) = GST_BUFFER_PTS (output_buffer);
1415       GST_BUFFER_DTS (output_buffer2) = GST_BUFFER_DTS (output_buffer);
1416       GST_BUFFER_DURATION (output_buffer2) =
1417           GST_BUFFER_DURATION (output_buffer);
1418 
1419       if (interlace->field_index == 0) {
1420         GST_BUFFER_FLAG_SET (output_buffer, GST_VIDEO_BUFFER_FLAG_TOP_FIELD);
1421         GST_BUFFER_FLAG_SET (output_buffer2,
1422             GST_VIDEO_BUFFER_FLAG_BOTTOM_FIELD);
1423       } else {
1424         GST_BUFFER_FLAG_SET (output_buffer, GST_VIDEO_BUFFER_FLAG_BOTTOM_FIELD);
1425         GST_BUFFER_FLAG_SET (output_buffer2, GST_VIDEO_BUFFER_FLAG_TOP_FIELD);
1426       }
1427 
1428       GST_BUFFER_FLAG_SET (output_buffer, GST_VIDEO_BUFFER_FLAG_INTERLACED);
1429       GST_BUFFER_FLAG_SET (output_buffer2, GST_VIDEO_BUFFER_FLAG_INTERLACED);
1430     }
1431 
1432     /* Guard against overflows here. If this ever happens, resetting the phase
1433      * above would never happen because of some bugs */
1434     g_assert (interlace->fields_since_timebase <= G_MAXUINT - n_output_fields);
1435     interlace->fields_since_timebase += n_output_fields;
1436     interlace->field_index ^= (n_output_fields & 1);
1437 
1438     ret = gst_interlace_push_buffer (interlace, output_buffer);
1439     if (ret != GST_FLOW_OK) {
1440       GST_DEBUG_OBJECT (interlace, "Failed to push buffer %p", output_buffer);
1441       break;
1442     }
1443 
1444     if (output_buffer2) {
1445       ret = gst_interlace_push_buffer (interlace, output_buffer2);
1446       if (ret != GST_FLOW_OK) {
1447         GST_DEBUG_OBJECT (interlace, "Failed to push buffer %p",
1448             output_buffer2);
1449         break;
1450       }
1451     }
1452   }
1453 
1454   GST_DEBUG ("done.  %d fields remaining", current_fields);
1455 
1456   if (interlace->stored_frame) {
1457     gst_buffer_unref (interlace->stored_frame);
1458     interlace->stored_frame = NULL;
1459     interlace->stored_fields = 0;
1460   }
1461 
1462   if (current_fields > 0) {
1463     interlace->stored_frame = buffer;
1464     interlace->stored_fields = current_fields;
1465   } else {
1466     gst_buffer_unref (buffer);
1467   }
1468   return ret;
1469 }
1470 
1471 static void
gst_interlace_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)1472 gst_interlace_set_property (GObject * object,
1473     guint prop_id, const GValue * value, GParamSpec * pspec)
1474 {
1475   GstInterlace *interlace = GST_INTERLACE (object);
1476 
1477   switch (prop_id) {
1478     case PROP_TOP_FIELD_FIRST:
1479       GST_OBJECT_LOCK (interlace);
1480       interlace->top_field_first = g_value_get_boolean (value);
1481       GST_OBJECT_UNLOCK (interlace);
1482       break;
1483     case PROP_PATTERN:{
1484       gint pattern = g_value_get_enum (value);
1485       gboolean reconfigure = FALSE;
1486 
1487       GST_OBJECT_LOCK (interlace);
1488       interlace->new_pattern = pattern;
1489       if (interlace->src_fps_n == 0 || interlace->pattern == pattern)
1490         interlace->pattern = pattern;
1491       else
1492         reconfigure = TRUE;
1493       GST_OBJECT_UNLOCK (interlace);
1494 
1495       if (reconfigure)
1496         gst_pad_push_event (interlace->sinkpad, gst_event_new_reconfigure ());
1497       break;
1498     }
1499     case PROP_PATTERN_OFFSET:
1500       GST_OBJECT_LOCK (interlace);
1501       interlace->pattern_offset = g_value_get_uint (value);
1502       GST_OBJECT_UNLOCK (interlace);
1503       break;
1504     case PROP_ALLOW_RFF:
1505       GST_OBJECT_LOCK (interlace);
1506       interlace->allow_rff = g_value_get_boolean (value);
1507       GST_OBJECT_UNLOCK (interlace);
1508       break;
1509     default:
1510       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1511       break;
1512   }
1513 }
1514 
1515 static void
gst_interlace_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)1516 gst_interlace_get_property (GObject * object,
1517     guint prop_id, GValue * value, GParamSpec * pspec)
1518 {
1519   GstInterlace *interlace = GST_INTERLACE (object);
1520 
1521   switch (prop_id) {
1522     case PROP_TOP_FIELD_FIRST:
1523       GST_OBJECT_LOCK (interlace);
1524       g_value_set_boolean (value, interlace->top_field_first);
1525       GST_OBJECT_UNLOCK (interlace);
1526       break;
1527     case PROP_PATTERN:
1528       GST_OBJECT_LOCK (interlace);
1529       g_value_set_enum (value, interlace->new_pattern);
1530       GST_OBJECT_UNLOCK (interlace);
1531       break;
1532     case PROP_PATTERN_OFFSET:
1533       GST_OBJECT_LOCK (interlace);
1534       g_value_set_uint (value, interlace->pattern_offset);
1535       GST_OBJECT_UNLOCK (interlace);
1536       break;
1537     case PROP_ALLOW_RFF:
1538       GST_OBJECT_LOCK (interlace);
1539       g_value_set_boolean (value, interlace->allow_rff);
1540       GST_OBJECT_UNLOCK (interlace);
1541       break;
1542     default:
1543       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1544       break;
1545   }
1546 }
1547 
1548 static GstStateChangeReturn
gst_interlace_change_state(GstElement * element,GstStateChange transition)1549 gst_interlace_change_state (GstElement * element, GstStateChange transition)
1550 {
1551   GstInterlace *interlace = GST_INTERLACE (element);
1552   GstStateChangeReturn ret;
1553 
1554   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1555 
1556   switch (transition) {
1557     case GST_STATE_CHANGE_PAUSED_TO_READY:
1558       GST_OBJECT_LOCK (interlace);
1559       interlace->src_fps_n = 0;
1560       interlace->src_fps_d = 1;
1561       GST_OBJECT_UNLOCK (interlace);
1562 
1563       gst_interlace_reset (interlace);
1564       break;
1565     default:
1566       break;
1567   }
1568 
1569   return ret;
1570 }
1571 
1572 static gboolean
plugin_init(GstPlugin * plugin)1573 plugin_init (GstPlugin * plugin)
1574 {
1575   return GST_ELEMENT_REGISTER (interlace, plugin);
1576 }
1577 
1578 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1579     GST_VERSION_MINOR,
1580     interlace,
1581     "Create an interlaced video stream",
1582     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
1583