documentation: fixed a heap o' typos
[platform/upstream/gstreamer.git] / sys / decklink / gstdecklinkvideosink.cpp
1 /* GStreamer
2  * Copyright (C) 2011 David Schleef <ds@entropywave.com>
3  * Copyright (C) 2014 Sebastian Dröge <sebastian@centricular.com>
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 Street, Suite 500,
18  * Boston, MA 02110-1335, USA.
19  */
20 /**
21  * SECTION:element-decklinkvideosink
22  * @short_description: Outputs Video to a BlackMagic DeckLink Device
23  *
24  * Playout Video to a BlackMagic DeckLink Device.
25  *
26  * ## Sample pipeline
27  * |[
28  * gst-launch-1.0 \
29  *   videotestsrc ! \
30  *   decklinkvideosink device-number=0 mode=1080p25
31  * ]|
32  * Playout a 1080p25 test-video to the SDI-Out of Card 0. Devices are numbered
33  * starting with 0.
34  *
35  * # Duplex-Mode:
36  * Certain DechLink Cards like the Duo2 or the Quad2 contain two or four
37  * independent SDI units with two connectors each. These units can operate either
38  * in half- or in full-duplex mode.
39  *
40  * The Duplex-Mode of a Card can be configured using the `duplex-mode`-Property.
41  * Cards that to not support Duplex-Modes are not influenced by the property.
42  *
43  * ## Half-Duplex-Mode (default):
44  * By default decklinkvideosink will configure them into half-duplex mode, so that
45  * each connector acts as if it were an independent DeckLink Card which can either
46  * be used as an Input or as an Output. In this mode the Duo2 can be used as as 4 SDI
47  * In-/Outputs and the Quad2 as 8 SDI In-/Outputs.
48  *
49  * |[
50  *  gst-launch-1.0 \
51  *    videotestsrc foreground-color=0x00ff0000 ! decklinkvideosink device-number=0 mode=1080p25 \
52  *    videotestsrc foreground-color=0x0000ff00 ! decklinkvideosink device-number=1 mode=1080p25 \
53  *    videotestsrc foreground-color=0x000000ff ! decklinkvideosink device-number=2 mode=1080p25 \
54  *    videotestsrc foreground-color=0x00ffffff ! decklinkvideosink device-number=3 mode=1080p25
55  * ]|
56  * Playout four Test-Screen with colored Snow on the first four units in the System
57  * (ie. the Connectors 1-4 of a Duo2 unit).
58  *
59  * |[
60  *  gst-launch-1.0 \
61  *    videotestsrc is-live=true foreground-color=0x0000ff00 ! decklinkvideosink device-number=0 mode=1080p25 \
62  *    decklinkvideosrc device-number=1 mode=1080p25 ! autovideosink \
63  *    decklinkvideosrc device-number=2 mode=1080p25 ! autovideosink \
64  *    videotestsrc is-live=true foreground-color=0x00ff0000 ! decklinkvideosink device-number=3 mode=1080p25
65  * ]|
66  * Capture 1080p25 from the second and third unit in the System,
67  * Playout a Test-Screen with colored Snow on the first and fourth unit
68  * (ie. the Connectors 1-4 of a Duo2 unit).
69  *
70  * ## Device-Number-Mapping in Half-Duplex-Mode
71  * The device-number to connector-mapping is as follows for the Duo2
72  * - `device-number=0` SDI1
73  * - `device-number=1` SDI3
74  * - `device-number=2` SDI2
75  * - `device-number=3` SDI4
76  *
77  * And for the Quad2
78  * - `device-number=0` SDI1
79  * - `device-number=1` SDI3
80  * - `device-number=2` SDI5
81  * - `device-number=3` SDI7
82  * - `device-number=4` SDI2
83  * - `device-number=5` SDI4
84  * - `device-number=6` SDI6
85  * - `device-number=7` SDI8
86  *
87  * ## Full-Duplex-Mode:
88  * When operating in full-duplex mode, two connectors of a unit are combined to
89  * a single device, performing keying with the second connection.
90  *
91  * ## Device-Number-Mapping in Full-Duplex-Mode
92  * The device-number to connector-mapping in full-duplex-mode is as follows for the Duo2
93  * - `device-number=0` SDI1 primary, SDI2 secondary
94  * - `device-number=1` SDI3 primaty, SDI4 secondary
95  *
96  * And for the Quad2
97  * - `device-number=0` SDI1 primary, SDI2 secondary
98  * - `device-number=1` SDI3 primaty, SDI4 secondary
99  * - `device-number=2` SDI5 primary, SDI6 secondary
100  * - `device-number=3` SDI7 primary, SDI8 secondary
101  *
102  * # Keying
103  * Keying is the process of overlaing Video with an Alpha-Channel on top of an
104  * existing Video-Stream. The Duo2 and Quad2-Cards can perform two different
105  * Keying-Modes when operated in full-duplex mode. Both modes expect Video with
106  * an Alpha-Channel.
107  *
108  * ## Internal Keyer:
109  * In internal Keying-Mode the primary port becomes an Input and the secondary port
110  * an Output. The unit overlays Video played back from the Computer onto the Input
111  * and outputs the combined Video-Stream to the Output.
112  *
113  * |[
114  * gst-launch-1.0 \
115  *  videotestsrc foreground-color=0x00000000 background-color=0x00000000 ! \
116  *  video/x-raw,format=BGRA,width=1920,height=1080 ! \
117  *  decklinkvideosink device-number=0 duplex-mode=full keyer-mode=internal video-format=8bit-bgra mode=1080p25
118  * ]|
119  *
120  * ## External Keyer:
121  * In external Keying-Mode the primary port outputs the alpha-chanel as the
122  * luma-value (key-channel). Transparent pixels are black, opaque pixels are white.
123  * The RGB-Component of the Video are output on the secondary channel.
124  *
125  * |[
126  * gst-launch-1.0 \
127  *  videotestsrc foreground-color=0x00000000 background-color=0x00000000 ! \
128  *  video/x-raw,format=BGRA,width=1920,height=1080 ! \
129  *  decklinkvideosink device-number=0 duplex-mode=full keyer-mode=external video-format=8bit-bgra mode=1080p25
130  * ]|
131  */
132
133 #ifdef HAVE_CONFIG_H
134 #include "config.h"
135 #endif
136
137 #include "gstdecklinkvideosink.h"
138 #include <string.h>
139
140 GST_DEBUG_CATEGORY_STATIC (gst_decklink_video_sink_debug);
141 #define GST_CAT_DEFAULT gst_decklink_video_sink_debug
142
143 class GStreamerVideoOutputCallback:public IDeckLinkVideoOutputCallback
144 {
145 public:
146   GStreamerVideoOutputCallback (GstDecklinkVideoSink * sink)
147   :IDeckLinkVideoOutputCallback (), m_refcount (1)
148   {
149     m_sink = GST_DECKLINK_VIDEO_SINK_CAST (gst_object_ref (sink));
150     g_mutex_init (&m_mutex);
151   }
152
153   virtual HRESULT WINAPI QueryInterface (REFIID, LPVOID *)
154   {
155     return E_NOINTERFACE;
156   }
157
158   virtual ULONG WINAPI AddRef (void)
159   {
160     ULONG ret;
161
162     g_mutex_lock (&m_mutex);
163     m_refcount++;
164     ret = m_refcount;
165     g_mutex_unlock (&m_mutex);
166
167     return ret;
168   }
169
170   virtual ULONG WINAPI Release (void)
171   {
172     ULONG ret;
173
174     g_mutex_lock (&m_mutex);
175     m_refcount--;
176     ret = m_refcount;
177     g_mutex_unlock (&m_mutex);
178
179     if (ret == 0) {
180       delete this;
181     }
182
183     return ret;
184   }
185
186   virtual HRESULT WINAPI ScheduledFrameCompleted (IDeckLinkVideoFrame *
187       completedFrame, BMDOutputFrameCompletionResult result)
188   {
189     switch (result) {
190       case bmdOutputFrameCompleted:
191         GST_LOG_OBJECT (m_sink, "Completed frame %p", completedFrame);
192         break;
193       case bmdOutputFrameDisplayedLate:
194         GST_INFO_OBJECT (m_sink, "Late Frame %p", completedFrame);
195         break;
196       case bmdOutputFrameDropped:
197         GST_INFO_OBJECT (m_sink, "Dropped Frame %p", completedFrame);
198         break;
199       case bmdOutputFrameFlushed:
200         GST_DEBUG_OBJECT (m_sink, "Flushed Frame %p", completedFrame);
201         break;
202       default:
203         GST_INFO_OBJECT (m_sink, "Unknown Frame %p: %d", completedFrame,
204             (gint) result);
205         break;
206     }
207
208     return S_OK;
209   }
210
211   virtual HRESULT WINAPI ScheduledPlaybackHasStopped (void)
212   {
213     GST_LOG_OBJECT (m_sink, "Scheduled playback stopped");
214
215     if (m_sink->output) {
216       g_mutex_lock (&m_sink->output->lock);
217       g_cond_signal (&m_sink->output->cond);
218       g_mutex_unlock (&m_sink->output->lock);
219     }
220
221     return S_OK;
222   }
223
224   virtual ~ GStreamerVideoOutputCallback () {
225     gst_object_unref (m_sink);
226     g_mutex_clear (&m_mutex);
227   }
228
229 private:
230   GstDecklinkVideoSink * m_sink;
231   GMutex m_mutex;
232   gint m_refcount;
233 };
234
235 enum
236 {
237   PROP_0,
238   PROP_MODE,
239   PROP_DEVICE_NUMBER,
240   PROP_VIDEO_FORMAT,
241   PROP_DUPLEX_MODE,
242   PROP_TIMECODE_FORMAT,
243   PROP_KEYER_MODE,
244   PROP_KEYER_LEVEL,
245   PROP_HW_SERIAL_NUMBER,
246   PROP_CC_LINE,
247   PROP_AFD_BAR_LINE,
248 };
249
250 static void gst_decklink_video_sink_set_property (GObject * object,
251     guint property_id, const GValue * value, GParamSpec * pspec);
252 static void gst_decklink_video_sink_get_property (GObject * object,
253     guint property_id, GValue * value, GParamSpec * pspec);
254 static void gst_decklink_video_sink_finalize (GObject * object);
255
256 static GstStateChangeReturn
257 gst_decklink_video_sink_change_state (GstElement * element,
258     GstStateChange transition);
259 static GstClock *gst_decklink_video_sink_provide_clock (GstElement * element);
260
261 static GstCaps *gst_decklink_video_sink_get_caps (GstBaseSink * bsink,
262     GstCaps * filter);
263 static gboolean gst_decklink_video_sink_set_caps (GstBaseSink * bsink,
264     GstCaps * caps);
265 static GstFlowReturn gst_decklink_video_sink_prepare (GstBaseSink * bsink,
266     GstBuffer * buffer);
267 static GstFlowReturn gst_decklink_video_sink_render (GstBaseSink * bsink,
268     GstBuffer * buffer);
269 static gboolean gst_decklink_video_sink_open (GstBaseSink * bsink);
270 static gboolean gst_decklink_video_sink_close (GstBaseSink * bsink);
271 static gboolean gst_decklink_video_sink_stop (GstDecklinkVideoSink * self);
272 static gboolean gst_decklink_video_sink_propose_allocation (GstBaseSink * bsink,
273     GstQuery * query);
274 static gboolean gst_decklink_video_sink_event (GstBaseSink * bsink,
275     GstEvent * event);
276
277 static void
278 gst_decklink_video_sink_start_scheduled_playback (GstElement * element);
279
280 #define parent_class gst_decklink_video_sink_parent_class
281 G_DEFINE_TYPE (GstDecklinkVideoSink, gst_decklink_video_sink,
282     GST_TYPE_BASE_SINK);
283
284 static gboolean
285 reset_framerate (GstCapsFeatures * features, GstStructure * structure,
286     gpointer user_data)
287 {
288   gst_structure_set (structure, "framerate", GST_TYPE_FRACTION_RANGE, 0, 1,
289       G_MAXINT, 1, NULL);
290
291   return TRUE;
292 }
293
294 static void
295 gst_decklink_video_sink_class_init (GstDecklinkVideoSinkClass * klass)
296 {
297   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
298   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
299   GstBaseSinkClass *basesink_class = GST_BASE_SINK_CLASS (klass);
300   GstCaps *templ_caps;
301
302   gobject_class->set_property = gst_decklink_video_sink_set_property;
303   gobject_class->get_property = gst_decklink_video_sink_get_property;
304   gobject_class->finalize = gst_decklink_video_sink_finalize;
305
306   element_class->change_state =
307       GST_DEBUG_FUNCPTR (gst_decklink_video_sink_change_state);
308   element_class->provide_clock =
309       GST_DEBUG_FUNCPTR (gst_decklink_video_sink_provide_clock);
310
311   basesink_class->get_caps =
312       GST_DEBUG_FUNCPTR (gst_decklink_video_sink_get_caps);
313   basesink_class->set_caps =
314       GST_DEBUG_FUNCPTR (gst_decklink_video_sink_set_caps);
315   basesink_class->prepare = GST_DEBUG_FUNCPTR (gst_decklink_video_sink_prepare);
316   basesink_class->render = GST_DEBUG_FUNCPTR (gst_decklink_video_sink_render);
317   // FIXME: These are misnamed in basesink!
318   basesink_class->start = GST_DEBUG_FUNCPTR (gst_decklink_video_sink_open);
319   basesink_class->stop = GST_DEBUG_FUNCPTR (gst_decklink_video_sink_close);
320   basesink_class->propose_allocation =
321       GST_DEBUG_FUNCPTR (gst_decklink_video_sink_propose_allocation);
322   basesink_class->event = GST_DEBUG_FUNCPTR (gst_decklink_video_sink_event);
323
324   g_object_class_install_property (gobject_class, PROP_MODE,
325       g_param_spec_enum ("mode", "Playback Mode",
326           "Video Mode to use for playback",
327           GST_TYPE_DECKLINK_MODE, GST_DECKLINK_MODE_NTSC,
328           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
329               G_PARAM_CONSTRUCT)));
330
331   g_object_class_install_property (gobject_class, PROP_DEVICE_NUMBER,
332       g_param_spec_int ("device-number", "Device number",
333           "Output device instance to use", 0, G_MAXINT, 0,
334           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
335               G_PARAM_CONSTRUCT)));
336
337   g_object_class_install_property (gobject_class, PROP_VIDEO_FORMAT,
338       g_param_spec_enum ("video-format", "Video format",
339           "Video format type to use for playback",
340           GST_TYPE_DECKLINK_VIDEO_FORMAT, GST_DECKLINK_VIDEO_FORMAT_8BIT_YUV,
341           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
342               G_PARAM_CONSTRUCT)));
343
344   g_object_class_install_property (gobject_class, PROP_DUPLEX_MODE,
345       g_param_spec_enum ("duplex-mode", "Duplex mode",
346           "Certain DeckLink devices such as the DeckLink Quad 2 and the "
347           "DeckLink Duo 2 support configuration of the duplex mode of "
348           "individual sub-devices."
349           "A sub-device configured as full-duplex will use two connectors, "
350           "which allows simultaneous capture and playback, internal keying, "
351           "and fill & key scenarios."
352           "A half-duplex sub-device will use a single connector as an "
353           "individual capture or playback channel.",
354           GST_TYPE_DECKLINK_DUPLEX_MODE, GST_DECKLINK_DUPLEX_MODE_HALF,
355           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
356               G_PARAM_CONSTRUCT)));
357
358   g_object_class_install_property (gobject_class, PROP_TIMECODE_FORMAT,
359       g_param_spec_enum ("timecode-format", "Timecode format",
360           "Timecode format type to use for playback",
361           GST_TYPE_DECKLINK_TIMECODE_FORMAT,
362           GST_DECKLINK_TIMECODE_FORMAT_RP188ANY,
363           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
364               G_PARAM_CONSTRUCT)));
365
366   g_object_class_install_property (gobject_class, PROP_KEYER_MODE,
367       g_param_spec_enum ("keyer-mode", "Keyer mode",
368           "Keyer mode to be enabled",
369           GST_TYPE_DECKLINK_KEYER_MODE,
370           GST_DECKLINK_KEYER_MODE_OFF,
371           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
372               G_PARAM_CONSTRUCT)));
373
374   g_object_class_install_property (gobject_class, PROP_KEYER_LEVEL,
375       g_param_spec_int ("keyer-level", "Keyer level",
376           "Keyer level", 0, 255, 255,
377           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
378               G_PARAM_CONSTRUCT)));
379
380   g_object_class_install_property (gobject_class, PROP_HW_SERIAL_NUMBER,
381       g_param_spec_string ("hw-serial-number", "Hardware serial number",
382           "The serial number (hardware ID) of the Decklink card",
383           NULL, (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
384
385   g_object_class_install_property (gobject_class, PROP_CC_LINE,
386       g_param_spec_int ("cc-line", "CC Line",
387           "Line number to use for inserting closed captions (0 = disabled)", 0,
388           22, 0,
389           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
390               G_PARAM_CONSTRUCT)));
391
392   g_object_class_install_property (gobject_class, PROP_AFD_BAR_LINE,
393       g_param_spec_int ("afd-bar-line", "AFD/Bar Line",
394           "Line number to use for inserting AFD/Bar data (0 = disabled)", 0,
395           10000, 0,
396           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
397               G_PARAM_CONSTRUCT)));
398
399   templ_caps = gst_decklink_mode_get_template_caps (FALSE);
400   templ_caps = gst_caps_make_writable (templ_caps);
401   /* For output we support any framerate and only really care about timestamps */
402   gst_caps_map_in_place (templ_caps, reset_framerate, NULL);
403   gst_element_class_add_pad_template (element_class,
404       gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, templ_caps));
405   gst_caps_unref (templ_caps);
406
407   gst_element_class_set_static_metadata (element_class, "Decklink Video Sink",
408       "Video/Sink/Hardware", "Decklink Sink",
409       "David Schleef <ds@entropywave.com>, "
410       "Sebastian Dröge <sebastian@centricular.com>");
411
412   GST_DEBUG_CATEGORY_INIT (gst_decklink_video_sink_debug, "decklinkvideosink",
413       0, "debug category for decklinkvideosink element");
414 }
415
416 static void
417 gst_decklink_video_sink_init (GstDecklinkVideoSink * self)
418 {
419   self->mode = GST_DECKLINK_MODE_NTSC;
420   self->device_number = 0;
421   self->video_format = GST_DECKLINK_VIDEO_FORMAT_8BIT_YUV;
422   self->duplex_mode = bmdDuplexModeHalf;
423   /* VITC is legacy, we should expect RP188 in modern use cases */
424   self->timecode_format = bmdTimecodeRP188Any;
425   self->caption_line = 0;
426   self->afd_bar_line = 0;
427
428   gst_base_sink_set_max_lateness (GST_BASE_SINK_CAST (self), 20 * GST_MSECOND);
429   gst_base_sink_set_qos_enabled (GST_BASE_SINK_CAST (self), TRUE);
430 }
431
432 void
433 gst_decklink_video_sink_set_property (GObject * object, guint property_id,
434     const GValue * value, GParamSpec * pspec)
435 {
436   GstDecklinkVideoSink *self = GST_DECKLINK_VIDEO_SINK_CAST (object);
437
438   switch (property_id) {
439     case PROP_MODE:
440       self->mode = (GstDecklinkModeEnum) g_value_get_enum (value);
441       break;
442     case PROP_DEVICE_NUMBER:
443       self->device_number = g_value_get_int (value);
444       break;
445     case PROP_VIDEO_FORMAT:
446       self->video_format = (GstDecklinkVideoFormat) g_value_get_enum (value);
447       switch (self->video_format) {
448         case GST_DECKLINK_VIDEO_FORMAT_AUTO:
449         case GST_DECKLINK_VIDEO_FORMAT_8BIT_YUV:
450         case GST_DECKLINK_VIDEO_FORMAT_10BIT_YUV:
451         case GST_DECKLINK_VIDEO_FORMAT_8BIT_ARGB:
452         case GST_DECKLINK_VIDEO_FORMAT_8BIT_BGRA:
453           break;
454         default:
455           GST_ELEMENT_WARNING (GST_ELEMENT (self), CORE, NOT_IMPLEMENTED,
456               ("Format %d not supported", self->video_format), (NULL));
457           break;
458       }
459       break;
460     case PROP_DUPLEX_MODE:
461       self->duplex_mode =
462           gst_decklink_duplex_mode_from_enum ((GstDecklinkDuplexMode)
463           g_value_get_enum (value));
464       break;
465     case PROP_TIMECODE_FORMAT:
466       self->timecode_format =
467           gst_decklink_timecode_format_from_enum ((GstDecklinkTimecodeFormat)
468           g_value_get_enum (value));
469       break;
470     case PROP_KEYER_MODE:
471       self->keyer_mode =
472           gst_decklink_keyer_mode_from_enum ((GstDecklinkKeyerMode)
473           g_value_get_enum (value));
474       break;
475     case PROP_KEYER_LEVEL:
476       self->keyer_level = g_value_get_int (value);
477       break;
478     case PROP_CC_LINE:
479       self->caption_line = g_value_get_int (value);
480       break;
481     case PROP_AFD_BAR_LINE:
482       self->afd_bar_line = g_value_get_int (value);
483       break;
484     default:
485       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
486       break;
487   }
488 }
489
490 void
491 gst_decklink_video_sink_get_property (GObject * object, guint property_id,
492     GValue * value, GParamSpec * pspec)
493 {
494   GstDecklinkVideoSink *self = GST_DECKLINK_VIDEO_SINK_CAST (object);
495
496   switch (property_id) {
497     case PROP_MODE:
498       g_value_set_enum (value, self->mode);
499       break;
500     case PROP_DEVICE_NUMBER:
501       g_value_set_int (value, self->device_number);
502       break;
503     case PROP_VIDEO_FORMAT:
504       g_value_set_enum (value, self->video_format);
505       break;
506     case PROP_DUPLEX_MODE:
507       g_value_set_enum (value,
508           gst_decklink_duplex_mode_to_enum (self->duplex_mode));
509       break;
510     case PROP_TIMECODE_FORMAT:
511       g_value_set_enum (value,
512           gst_decklink_timecode_format_to_enum (self->timecode_format));
513       break;
514     case PROP_KEYER_MODE:
515       g_value_set_enum (value,
516           gst_decklink_keyer_mode_to_enum (self->keyer_mode));
517       break;
518     case PROP_KEYER_LEVEL:
519       g_value_set_int (value, self->keyer_level);
520       break;
521     case PROP_HW_SERIAL_NUMBER:
522       if (self->output)
523         g_value_set_string (value, self->output->hw_serial_number);
524       else
525         g_value_set_string (value, NULL);
526       break;
527     case PROP_CC_LINE:
528       g_value_set_int (value, self->caption_line);
529       break;
530     case PROP_AFD_BAR_LINE:
531       g_value_set_int (value, self->afd_bar_line);
532       break;
533     default:
534       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
535       break;
536   }
537 }
538
539 void
540 gst_decklink_video_sink_finalize (GObject * object)
541 {
542   //GstDecklinkVideoSink *self = GST_DECKLINK_VIDEO_SINK_CAST (object);
543
544   G_OBJECT_CLASS (parent_class)->finalize (object);
545 }
546
547 static gboolean
548 gst_decklink_video_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
549 {
550   GstDecklinkVideoSink *self = GST_DECKLINK_VIDEO_SINK_CAST (bsink);
551   const GstDecklinkMode *mode;
552   HRESULT ret;
553   BMDVideoOutputFlags flags;
554   GstVideoInfo info;
555
556   GST_DEBUG_OBJECT (self, "Setting caps %" GST_PTR_FORMAT, caps);
557
558   if (!gst_video_info_from_caps (&info, caps))
559     return FALSE;
560
561
562   g_mutex_lock (&self->output->lock);
563   if (self->output->video_enabled) {
564     if (self->info.finfo->format == info.finfo->format &&
565         self->info.width == info.width && self->info.height == info.height) {
566       // FIXME: We should also consider the framerate as it is used
567       // for mode selection below in auto mode
568       GST_DEBUG_OBJECT (self, "Nothing relevant has changed");
569       self->info = info;
570       g_mutex_unlock (&self->output->lock);
571       return TRUE;
572     } else {
573       GST_DEBUG_OBJECT (self, "Reconfiguration not supported at this point");
574       g_mutex_unlock (&self->output->lock);
575       return FALSE;
576     }
577   }
578   g_mutex_unlock (&self->output->lock);
579
580   self->output->output->SetScheduledFrameCompletionCallback (new
581       GStreamerVideoOutputCallback (self));
582
583   if (self->mode == GST_DECKLINK_MODE_AUTO) {
584     BMDPixelFormat f;
585     mode = gst_decklink_find_mode_and_format_for_caps (caps, &f);
586     if (mode == NULL) {
587       GST_WARNING_OBJECT (self,
588           "Failed to find compatible mode for caps  %" GST_PTR_FORMAT, caps);
589       return FALSE;
590     }
591     if (self->video_format != GST_DECKLINK_VIDEO_FORMAT_AUTO &&
592         gst_decklink_pixel_format_from_type (self->video_format) != f) {
593       GST_WARNING_OBJECT (self, "Failed to set pixel format to %d",
594           self->video_format);
595       return FALSE;
596     }
597   } else {
598     /* We don't have to give the format in EnableVideoOutput. Therefore,
599      * even if it's AUTO, we have it stored in self->info and set it in
600      * gst_decklink_video_sink_prepare */
601     mode = gst_decklink_get_mode (self->mode);
602     g_assert (mode != NULL);
603   };
604
605   /* enable or disable keyer */
606   if (self->output->keyer != NULL) {
607     if (self->keyer_mode == bmdKeyerModeOff) {
608       self->output->keyer->Disable ();
609     } else if (self->keyer_mode == bmdKeyerModeInternal) {
610       self->output->keyer->Enable (false);
611       self->output->keyer->SetLevel (self->keyer_level);
612     } else if (self->keyer_mode == bmdKeyerModeExternal) {
613       self->output->keyer->Enable (true);
614       self->output->keyer->SetLevel (self->keyer_level);
615     } else {
616       g_assert_not_reached ();
617     }
618   } else if (self->keyer_mode != bmdKeyerModeOff) {
619     GST_WARNING_OBJECT (self, "Failed to set keyer to mode %d",
620         self->keyer_mode);
621   }
622
623   /* The timecode_format itself is used when we embed the actual timecode data
624    * into the frame. Now we only need to know which of the two standards the
625    * timecode format will adhere to: VITC or RP188, and send the appropriate
626    * flag to EnableVideoOutput. The exact format is specified later.
627    *
628    * Note that this flag will have no effect in practice if the video stream
629    * does not contain timecode metadata.
630    */
631   if ((gint64) self->timecode_format ==
632       (gint64) GST_DECKLINK_TIMECODE_FORMAT_VITC
633       || (gint64) self->timecode_format ==
634       (gint64) GST_DECKLINK_TIMECODE_FORMAT_VITCFIELD2)
635     flags = bmdVideoOutputVITC;
636   else
637     flags = bmdVideoOutputRP188;
638
639   if (self->caption_line > 0 || self->afd_bar_line > 0)
640     flags = (BMDVideoOutputFlags) (flags | bmdVideoOutputVANC);
641
642   ret = self->output->output->EnableVideoOutput (mode->mode, flags);
643   if (ret != S_OK) {
644     GST_WARNING_OBJECT (self, "Failed to enable video output: 0x%08lx",
645         (unsigned long) ret);
646     return FALSE;
647   }
648
649   self->info = info;
650   g_mutex_lock (&self->output->lock);
651   self->output->mode = mode;
652   self->output->video_enabled = TRUE;
653   if (self->output->start_scheduled_playback)
654     self->output->start_scheduled_playback (self->output->videosink);
655   g_mutex_unlock (&self->output->lock);
656
657   if (self->vbiencoder) {
658     gst_video_vbi_encoder_free (self->vbiencoder);
659     self->vbiencoder = NULL;
660     self->anc_vformat = GST_VIDEO_FORMAT_UNKNOWN;
661   }
662
663   return TRUE;
664 }
665
666 static GstCaps *
667 gst_decklink_video_sink_get_caps (GstBaseSink * bsink, GstCaps * filter)
668 {
669   GstDecklinkVideoSink *self = GST_DECKLINK_VIDEO_SINK_CAST (bsink);
670   GstCaps *mode_caps, *caps;
671
672   if (self->mode == GST_DECKLINK_MODE_AUTO
673       && self->video_format == GST_DECKLINK_VIDEO_FORMAT_AUTO)
674     mode_caps = gst_decklink_mode_get_template_caps (FALSE);
675   else if (self->video_format == GST_DECKLINK_VIDEO_FORMAT_AUTO)
676     mode_caps = gst_decklink_mode_get_caps_all_formats (self->mode, FALSE);
677   else if (self->mode == GST_DECKLINK_MODE_AUTO)
678     mode_caps =
679         gst_decklink_pixel_format_get_caps (gst_decklink_pixel_format_from_type
680         (self->video_format), FALSE);
681   else
682     mode_caps =
683         gst_decklink_mode_get_caps (self->mode,
684         gst_decklink_pixel_format_from_type (self->video_format), FALSE);
685   mode_caps = gst_caps_make_writable (mode_caps);
686   /* For output we support any framerate and only really care about timestamps */
687   gst_caps_map_in_place (mode_caps, reset_framerate, NULL);
688
689   if (filter) {
690     caps =
691         gst_caps_intersect_full (filter, mode_caps, GST_CAPS_INTERSECT_FIRST);
692     gst_caps_unref (mode_caps);
693   } else {
694     caps = mode_caps;
695   }
696
697   return caps;
698 }
699
700 static GstFlowReturn
701 gst_decklink_video_sink_render (GstBaseSink * bsink, GstBuffer * buffer)
702 {
703   return GST_FLOW_OK;
704 }
705
706 void
707 gst_decklink_video_sink_convert_to_internal_clock (GstDecklinkVideoSink * self,
708     GstClockTime * timestamp, GstClockTime * duration)
709 {
710   GstClock *clock;
711   GstClockTime internal_base, external_base, internal_offset;
712
713   g_assert (timestamp != NULL);
714
715   clock = gst_element_get_clock (GST_ELEMENT_CAST (self));
716   GST_OBJECT_LOCK (self);
717   internal_base = self->internal_base_time;
718   external_base = self->external_base_time;
719   internal_offset = self->internal_time_offset;
720   GST_OBJECT_UNLOCK (self);
721
722   if (!clock || clock != self->output->clock) {
723     GstClockTime internal, external, rate_n, rate_d;
724     GstClockTime external_timestamp = *timestamp;
725     GstClockTime base_time;
726
727     gst_clock_get_calibration (self->output->clock, &internal, &external,
728         &rate_n, &rate_d);
729
730     // Convert to the running time corresponding to both clock times
731     if (!GST_CLOCK_TIME_IS_VALID (internal_base) || internal < internal_base)
732       internal = 0;
733     else
734       internal -= internal_base;
735
736     if (!GST_CLOCK_TIME_IS_VALID (external_base) || external < external_base)
737       external = 0;
738     else
739       external -= external_base;
740
741     // Convert timestamp to the "running time" since we started scheduled
742     // playback, that is the difference between the pipeline's base time
743     // and our own base time.
744     base_time = gst_element_get_base_time (GST_ELEMENT_CAST (self));
745     if (base_time > external_base)
746       base_time = 0;
747     else
748       base_time = external_base - base_time;
749
750     if (external_timestamp < base_time)
751       external_timestamp = 0;
752     else
753       external_timestamp = external_timestamp - base_time;
754
755     // Get the difference in the external time, note
756     // that the running time is external time.
757     // Then scale this difference and offset it to
758     // our internal time. Now we have the running time
759     // according to our internal clock.
760     //
761     // For the duration we just scale
762     *timestamp =
763         gst_clock_unadjust_with_calibration (NULL, external_timestamp,
764         internal, external, rate_n, rate_d);
765
766     GST_LOG_OBJECT (self,
767         "Converted %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT " (internal: %"
768         GST_TIME_FORMAT " external %" GST_TIME_FORMAT " rate: %lf)",
769         GST_TIME_ARGS (external_timestamp), GST_TIME_ARGS (*timestamp),
770         GST_TIME_ARGS (internal), GST_TIME_ARGS (external),
771         ((gdouble) rate_n) / ((gdouble) rate_d));
772
773     if (duration) {
774       GstClockTime external_duration = *duration;
775
776       *duration = gst_util_uint64_scale (external_duration, rate_d, rate_n);
777
778       GST_LOG_OBJECT (self,
779           "Converted duration %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT
780           " (internal: %" GST_TIME_FORMAT " external %" GST_TIME_FORMAT
781           " rate: %lf)", GST_TIME_ARGS (external_duration),
782           GST_TIME_ARGS (*duration), GST_TIME_ARGS (internal),
783           GST_TIME_ARGS (external), ((gdouble) rate_n) / ((gdouble) rate_d));
784     }
785   } else {
786     GST_LOG_OBJECT (self, "No clock conversion needed, same clocks: %"
787         GST_TIME_FORMAT, GST_TIME_ARGS (*timestamp));
788   }
789
790   if (external_base != GST_CLOCK_TIME_NONE &&
791       internal_base != GST_CLOCK_TIME_NONE)
792     *timestamp += internal_offset;
793   else
794     *timestamp = gst_clock_get_internal_time (self->output->clock);
795
796   GST_DEBUG_OBJECT (self, "Output timestamp %" GST_TIME_FORMAT
797       " using clock epoch %" GST_TIME_FORMAT,
798       GST_TIME_ARGS (*timestamp), GST_TIME_ARGS (self->output->clock_epoch));
799
800   if (clock)
801     gst_object_unref (clock);
802 }
803
804 /* Copied from ext/closedcaption/gstccconverter.c */
805 /* Converts raw CEA708 cc_data and an optional timecode into CDP */
806 static guint
807 convert_cea708_cc_data_cea708_cdp_internal (GstDecklinkVideoSink * self,
808     const guint8 * cc_data, guint cc_data_len, guint8 * cdp, guint cdp_len,
809     const GstVideoTimeCodeMeta * tc_meta)
810 {
811   GstByteWriter bw;
812   guint8 flags, checksum;
813   guint i, len;
814   const GstDecklinkMode *mode = gst_decklink_get_mode (self->mode);
815
816   gst_byte_writer_init_with_data (&bw, cdp, cdp_len, FALSE);
817   gst_byte_writer_put_uint16_be_unchecked (&bw, 0x9669);
818   /* Write a length of 0 for now */
819   gst_byte_writer_put_uint8_unchecked (&bw, 0);
820   if (mode->fps_n == 24000 && mode->fps_d == 1001) {
821     gst_byte_writer_put_uint8_unchecked (&bw, 0x1f);
822   } else if (mode->fps_n == 24 && mode->fps_d == 1) {
823     gst_byte_writer_put_uint8_unchecked (&bw, 0x2f);
824   } else if (mode->fps_n == 25 && mode->fps_d == 1) {
825     gst_byte_writer_put_uint8_unchecked (&bw, 0x3f);
826   } else if (mode->fps_n == 30 && mode->fps_d == 1001) {
827     gst_byte_writer_put_uint8_unchecked (&bw, 0x4f);
828   } else if (mode->fps_n == 30 && mode->fps_d == 1) {
829     gst_byte_writer_put_uint8_unchecked (&bw, 0x5f);
830   } else if (mode->fps_n == 50 && mode->fps_d == 1) {
831     gst_byte_writer_put_uint8_unchecked (&bw, 0x6f);
832   } else if (mode->fps_n == 60000 && mode->fps_d == 1001) {
833     gst_byte_writer_put_uint8_unchecked (&bw, 0x7f);
834   } else if (mode->fps_n == 60 && mode->fps_d == 1) {
835     gst_byte_writer_put_uint8_unchecked (&bw, 0x8f);
836   } else {
837     g_assert_not_reached ();
838   }
839
840   /* ccdata_present | caption_service_active */
841   flags = 0x42;
842
843   /* time_code_present */
844   if (tc_meta)
845     flags |= 0x80;
846
847   /* reserved */
848   flags |= 0x01;
849
850   gst_byte_writer_put_uint8_unchecked (&bw, flags);
851
852   gst_byte_writer_put_uint16_be_unchecked (&bw, self->cdp_hdr_sequence_cntr);
853
854   if (tc_meta) {
855     const GstVideoTimeCode *tc = &tc_meta->tc;
856
857     gst_byte_writer_put_uint8_unchecked (&bw, 0x71);
858     gst_byte_writer_put_uint8_unchecked (&bw, 0xc0 |
859         (((tc->hours % 10) & 0x3) << 4) |
860         ((tc->hours - (tc->hours % 10)) & 0xf));
861
862     gst_byte_writer_put_uint8_unchecked (&bw, 0x80 |
863         (((tc->minutes % 10) & 0x7) << 4) |
864         ((tc->minutes - (tc->minutes % 10)) & 0xf));
865
866     gst_byte_writer_put_uint8_unchecked (&bw,
867         (tc->field_count <
868             2 ? 0x00 : 0x80) | (((tc->seconds %
869                     10) & 0x7) << 4) | ((tc->seconds -
870                 (tc->seconds % 10)) & 0xf));
871
872     gst_byte_writer_put_uint8_unchecked (&bw,
873         ((tc->config.flags & GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME) ? 0x80 :
874             0x00) | (((tc->frames % 10) & 0x3) << 4) | ((tc->frames -
875                 (tc->frames % 10)) & 0xf));
876   }
877
878   gst_byte_writer_put_uint8_unchecked (&bw, 0x72);
879   gst_byte_writer_put_uint8_unchecked (&bw, 0xe0 | cc_data_len / 3);
880   gst_byte_writer_put_data_unchecked (&bw, cc_data, cc_data_len);
881
882   gst_byte_writer_put_uint8_unchecked (&bw, 0x74);
883   gst_byte_writer_put_uint16_be_unchecked (&bw, self->cdp_hdr_sequence_cntr);
884   self->cdp_hdr_sequence_cntr++;
885   /* We calculate the checksum afterwards */
886   gst_byte_writer_put_uint8_unchecked (&bw, 0);
887
888   len = gst_byte_writer_get_pos (&bw);
889   gst_byte_writer_set_pos (&bw, 2);
890   gst_byte_writer_put_uint8_unchecked (&bw, len);
891
892   checksum = 0;
893   for (i = 0; i < len; i++) {
894     checksum += cdp[i];
895   }
896   checksum &= 0xff;
897   checksum = 256 - checksum;
898   cdp[len - 1] = checksum;
899
900   return len;
901 }
902
903 static void
904 write_vbi (GstDecklinkVideoSink * self, GstBuffer * buffer,
905     BMDPixelFormat format, IDeckLinkMutableVideoFrame * frame,
906     GstVideoTimeCodeMeta * tc_meta)
907 {
908   IDeckLinkVideoFrameAncillary *vanc_frame = NULL;
909   gpointer iter = NULL;
910   GstVideoCaptionMeta *cc_meta;
911   guint8 *vancdata;
912   gboolean got_captions = FALSE;
913
914   if (self->caption_line == 0 && self->afd_bar_line == 0)
915     return;
916
917   if (self->vbiencoder == NULL) {
918     self->vbiencoder =
919         gst_video_vbi_encoder_new (self->info.finfo->format, self->info.width);
920     self->anc_vformat = self->info.finfo->format;
921   }
922
923   /* Put any closed captions into the configured line */
924   while ((cc_meta =
925           (GstVideoCaptionMeta *) gst_buffer_iterate_meta_filtered (buffer,
926               &iter, GST_VIDEO_CAPTION_META_API_TYPE))) {
927     switch (cc_meta->caption_type) {
928       case GST_VIDEO_CAPTION_TYPE_CEA608_RAW:{
929         guint8 data[138];
930         guint i, n;
931
932         n = cc_meta->size / 2;
933         if (cc_meta->size > 46) {
934           GST_WARNING_OBJECT (self, "Too big raw CEA608 buffer");
935           break;
936         }
937
938         /* This is the offset from line 9 for 525-line fields and from line
939          * 5 for 625-line fields.
940          *
941          * The highest bit is set for field 1 but not for field 0, but we
942          * have no way of knowning the field here
943          */
944         for (i = 0; i < n; i++) {
945           data[3 * i] = 0x80 | (self->info.height ==
946               525 ? self->caption_line - 9 : self->caption_line - 5);
947           data[3 * i + 1] = cc_meta->data[2 * i];
948           data[3 * i + 2] = cc_meta->data[2 * i + 1];
949         }
950
951         if (!gst_video_vbi_encoder_add_ancillary (self->vbiencoder,
952                 FALSE,
953                 GST_VIDEO_ANCILLARY_DID16_S334_EIA_608 >> 8,
954                 GST_VIDEO_ANCILLARY_DID16_S334_EIA_608 & 0xff, data, 3))
955           GST_WARNING_OBJECT (self, "Couldn't add meta to ancillary data");
956
957         got_captions = TRUE;
958
959         break;
960       }
961       case GST_VIDEO_CAPTION_TYPE_CEA608_S334_1A:{
962         if (!gst_video_vbi_encoder_add_ancillary (self->vbiencoder,
963                 FALSE,
964                 GST_VIDEO_ANCILLARY_DID16_S334_EIA_608 >> 8,
965                 GST_VIDEO_ANCILLARY_DID16_S334_EIA_608 & 0xff, cc_meta->data,
966                 cc_meta->size))
967           GST_WARNING_OBJECT (self, "Couldn't add meta to ancillary data");
968
969         got_captions = TRUE;
970
971         break;
972       }
973       case GST_VIDEO_CAPTION_TYPE_CEA708_RAW:{
974         guint8 data[256];
975         guint n;
976
977         n = cc_meta->size / 3;
978         if (cc_meta->size > 46) {
979           GST_WARNING_OBJECT (self, "Too big raw CEA708 buffer");
980           break;
981         }
982
983         n = convert_cea708_cc_data_cea708_cdp_internal (self, cc_meta->data,
984             cc_meta->size, data, sizeof (data), tc_meta);
985         if (!gst_video_vbi_encoder_add_ancillary (self->vbiencoder, FALSE,
986                 GST_VIDEO_ANCILLARY_DID16_S334_EIA_708 >> 8,
987                 GST_VIDEO_ANCILLARY_DID16_S334_EIA_708 & 0xff, data, n))
988           GST_WARNING_OBJECT (self, "Couldn't add meta to ancillary data");
989
990         got_captions = TRUE;
991
992         break;
993       }
994       case GST_VIDEO_CAPTION_TYPE_CEA708_CDP:{
995         if (!gst_video_vbi_encoder_add_ancillary (self->vbiencoder,
996                 FALSE,
997                 GST_VIDEO_ANCILLARY_DID16_S334_EIA_708 >> 8,
998                 GST_VIDEO_ANCILLARY_DID16_S334_EIA_708 & 0xff, cc_meta->data,
999                 cc_meta->size))
1000           GST_WARNING_OBJECT (self, "Couldn't add meta to ancillary data");
1001
1002         got_captions = TRUE;
1003
1004         break;
1005       }
1006       default:{
1007         GST_FIXME_OBJECT (self, "Caption type %d not supported",
1008             cc_meta->caption_type);
1009         break;
1010       }
1011     }
1012   }
1013
1014   if ((got_captions || self->afd_bar_line != 0)
1015       && self->output->output->CreateAncillaryData (format,
1016           &vanc_frame) == S_OK) {
1017     GstVideoAFDMeta *afd_meta = NULL, *afd_meta2 = NULL;
1018     GstVideoBarMeta *bar_meta = NULL, *bar_meta2 = NULL;
1019     GstMeta *meta;
1020     gpointer meta_iter;
1021     guint8 afd_bar_data[8] = { 0, };
1022     guint8 afd_bar_data2[8] = { 0, };
1023     guint8 afd = 0;
1024     gboolean is_letterbox = 0;
1025     guint16 bar1 = 0, bar2 = 0;
1026     guint i;
1027
1028     // Get any reasonable AFD/Bar metas for both fields
1029     meta_iter = NULL;
1030     while ((meta =
1031             gst_buffer_iterate_meta_filtered (buffer, &meta_iter,
1032                 GST_VIDEO_AFD_META_API_TYPE))) {
1033       GstVideoAFDMeta *tmp_meta = (GstVideoAFDMeta *) meta;
1034
1035       if (tmp_meta->field == 0 || !afd_meta || (afd_meta && afd_meta->field != 0
1036               && tmp_meta->field == 0))
1037         afd_meta = tmp_meta;
1038       if (tmp_meta->field == 1 || !afd_meta2 || (afd_meta2
1039               && afd_meta->field != 1 && tmp_meta->field == 1))
1040         afd_meta2 = tmp_meta;
1041     }
1042
1043     meta_iter = NULL;
1044     while ((meta =
1045             gst_buffer_iterate_meta_filtered (buffer, &meta_iter,
1046                 GST_VIDEO_BAR_META_API_TYPE))) {
1047       GstVideoBarMeta *tmp_meta = (GstVideoBarMeta *) meta;
1048
1049       if (tmp_meta->field == 0 || !bar_meta || (bar_meta && bar_meta->field != 0
1050               && tmp_meta->field == 0))
1051         bar_meta = tmp_meta;
1052       if (tmp_meta->field == 1 || !bar_meta2 || (bar_meta2
1053               && bar_meta->field != 1 && tmp_meta->field == 1))
1054         bar_meta2 = tmp_meta;
1055     }
1056
1057     for (i = 0; i < 2; i++) {
1058       guint8 *afd_bar_data_ptr;
1059
1060       if (i == 0) {
1061         afd_bar_data_ptr = afd_bar_data;
1062         afd = afd_meta ? afd_meta->afd : 0;
1063         is_letterbox = bar_meta ? bar_meta->is_letterbox : FALSE;
1064         bar1 = bar_meta ? bar_meta->bar_data1 : 0;
1065         bar2 = bar_meta ? bar_meta->bar_data2 : 0;
1066       } else {
1067         afd_bar_data_ptr = afd_bar_data2;
1068         afd = afd_meta2 ? afd_meta2->afd : 0;
1069         is_letterbox = bar_meta2 ? bar_meta2->is_letterbox : FALSE;
1070         bar1 = bar_meta2 ? bar_meta2->bar_data1 : 0;
1071         bar2 = bar_meta2 ? bar_meta2->bar_data2 : 0;
1072       }
1073
1074       /* See SMPTE 2016-3 Section 4 */
1075       /* AFD and AR */
1076       if (self->mode < (gint) GST_DECKLINK_MODE_NTSC_WIDESCREEN) {
1077         afd_bar_data_ptr[0] = (afd << 3) | 0x0;
1078       } else {
1079         afd_bar_data_ptr[0] = (afd << 3) | 0x4;
1080       }
1081
1082       /* Bar flags */
1083       afd_bar_data_ptr[3] = is_letterbox ? 0xc0 : 0x30;
1084
1085       /* Bar value 1 and 2 */
1086       GST_WRITE_UINT16_BE (&afd_bar_data_ptr[4], bar1);
1087       GST_WRITE_UINT16_BE (&afd_bar_data_ptr[6], bar2);
1088     }
1089
1090     /* AFD on the same line as the captions */
1091     if (self->caption_line == self->afd_bar_line) {
1092       if (!gst_video_vbi_encoder_add_ancillary (self->vbiencoder,
1093               FALSE, GST_VIDEO_ANCILLARY_DID16_S2016_3_AFD_BAR >> 8,
1094               GST_VIDEO_ANCILLARY_DID16_S2016_3_AFD_BAR & 0xff, afd_bar_data,
1095               sizeof (afd_bar_data)))
1096         GST_WARNING_OBJECT (self,
1097             "Couldn't add AFD/Bar data to ancillary data");
1098     }
1099
1100     /* FIXME: Add captions to the correct field? Captions for the second
1101      * field should probably be inserted into the second field */
1102
1103     if (got_captions || self->caption_line == self->afd_bar_line) {
1104       if (vanc_frame->GetBufferForVerticalBlankingLine (self->caption_line,
1105               (void **) &vancdata) == S_OK) {
1106         gst_video_vbi_encoder_write_line (self->vbiencoder, vancdata);
1107       } else {
1108         GST_WARNING_OBJECT (self,
1109             "Failed to get buffer for line %d ancillary data",
1110             self->caption_line);
1111       }
1112     }
1113
1114     /* AFD on a different line than the captions */
1115     if (self->afd_bar_line != 0 && self->caption_line != self->afd_bar_line) {
1116       if (!gst_video_vbi_encoder_add_ancillary (self->vbiencoder,
1117               FALSE, GST_VIDEO_ANCILLARY_DID16_S2016_3_AFD_BAR >> 8,
1118               GST_VIDEO_ANCILLARY_DID16_S2016_3_AFD_BAR & 0xff, afd_bar_data,
1119               sizeof (afd_bar_data)))
1120         GST_WARNING_OBJECT (self,
1121             "Couldn't add AFD/Bar data to ancillary data");
1122
1123       if (vanc_frame->GetBufferForVerticalBlankingLine (self->afd_bar_line,
1124               (void **) &vancdata) == S_OK) {
1125         gst_video_vbi_encoder_write_line (self->vbiencoder, vancdata);
1126       } else {
1127         GST_WARNING_OBJECT (self,
1128             "Failed to get buffer for line %d ancillary data",
1129             self->afd_bar_line);
1130       }
1131     }
1132
1133     /* For interlaced video we need to also add AFD to the second field */
1134     if (GST_VIDEO_INFO_IS_INTERLACED (&self->info) && self->afd_bar_line != 0) {
1135       guint field2_offset;
1136
1137       /* The VANC lines for the second field are at an offset, depending on
1138        * the format in use.
1139        */
1140       switch (self->info.height) {
1141         case 486:
1142           /* NTSC: 525 / 2 + 1 */
1143           field2_offset = 263;
1144           break;
1145         case 576:
1146           /* PAL: 625 / 2 + 1 */
1147           field2_offset = 313;
1148           break;
1149         case 1080:
1150           /* 1080i: 1125 / 2 + 1 */
1151           field2_offset = 563;
1152           break;
1153         default:
1154           g_assert_not_reached ();
1155       }
1156
1157       if (!gst_video_vbi_encoder_add_ancillary (self->vbiencoder,
1158               FALSE, GST_VIDEO_ANCILLARY_DID16_S2016_3_AFD_BAR >> 8,
1159               GST_VIDEO_ANCILLARY_DID16_S2016_3_AFD_BAR & 0xff, afd_bar_data2,
1160               sizeof (afd_bar_data)))
1161         GST_WARNING_OBJECT (self,
1162             "Couldn't add AFD/Bar data to ancillary data");
1163
1164       if (vanc_frame->GetBufferForVerticalBlankingLine (self->afd_bar_line +
1165               field2_offset, (void **) &vancdata) == S_OK) {
1166         gst_video_vbi_encoder_write_line (self->vbiencoder, vancdata);
1167       } else {
1168         GST_WARNING_OBJECT (self,
1169             "Failed to get buffer for line %d ancillary data",
1170             self->afd_bar_line);
1171       }
1172     }
1173
1174     if (frame->SetAncillaryData (vanc_frame) != S_OK) {
1175       GST_WARNING_OBJECT (self, "Failed to set ancillary data");
1176     }
1177
1178     vanc_frame->Release ();
1179   } else if (got_captions || self->afd_bar_line != 0) {
1180     GST_WARNING_OBJECT (self, "Failed to allocate ancillary data frame");
1181   }
1182 }
1183
1184 static GstFlowReturn
1185 gst_decklink_video_sink_prepare (GstBaseSink * bsink, GstBuffer * buffer)
1186 {
1187   GstDecklinkVideoSink *self = GST_DECKLINK_VIDEO_SINK_CAST (bsink);
1188   GstVideoFrame vframe;
1189   IDeckLinkMutableVideoFrame *frame;
1190   guint8 *outdata, *indata;
1191   GstFlowReturn flow_ret;
1192   HRESULT ret;
1193   GstClockTime timestamp, duration;
1194   GstClockTime running_time, running_time_duration;
1195   GstClockTime latency, render_delay;
1196   GstClockTimeDiff ts_offset;
1197   gint i;
1198   GstDecklinkVideoFormat caps_format;
1199   BMDPixelFormat format;
1200   gint stride;
1201   GstVideoTimeCodeMeta *tc_meta;
1202
1203   GST_DEBUG_OBJECT (self, "Preparing buffer %" GST_PTR_FORMAT, buffer);
1204
1205   // FIXME: Handle no timestamps
1206   if (!GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) {
1207     return GST_FLOW_ERROR;
1208   }
1209
1210   caps_format = gst_decklink_type_from_video_format (self->info.finfo->format);
1211   format = gst_decklink_pixel_format_from_type (caps_format);
1212
1213   timestamp = GST_BUFFER_TIMESTAMP (buffer);
1214   duration = GST_BUFFER_DURATION (buffer);
1215   if (duration == GST_CLOCK_TIME_NONE) {
1216     duration =
1217         gst_util_uint64_scale_int (GST_SECOND, self->info.fps_d,
1218         self->info.fps_n);
1219   }
1220   running_time =
1221       gst_segment_to_running_time (&GST_BASE_SINK_CAST (self)->segment,
1222       GST_FORMAT_TIME, timestamp);
1223   running_time_duration =
1224       gst_segment_to_running_time (&GST_BASE_SINK_CAST (self)->segment,
1225       GST_FORMAT_TIME, timestamp + duration) - running_time;
1226
1227   /* See gst_base_sink_adjust_time() */
1228   latency = gst_base_sink_get_latency (bsink);
1229   render_delay = gst_base_sink_get_render_delay (bsink);
1230   ts_offset = gst_base_sink_get_ts_offset (bsink);
1231
1232   running_time += latency;
1233
1234   if (ts_offset < 0) {
1235     ts_offset = -ts_offset;
1236     if ((GstClockTime) ts_offset < running_time)
1237       running_time -= ts_offset;
1238     else
1239       running_time = 0;
1240   } else {
1241     running_time += ts_offset;
1242   }
1243
1244   if (running_time > render_delay)
1245     running_time -= render_delay;
1246   else
1247     running_time = 0;
1248
1249   ret = self->output->output->CreateVideoFrame (self->info.width,
1250       self->info.height, self->info.stride[0], format, bmdFrameFlagDefault,
1251       &frame);
1252   if (ret != S_OK) {
1253     GST_ELEMENT_ERROR (self, STREAM, FAILED,
1254         (NULL), ("Failed to create video frame: 0x%08lx", (unsigned long) ret));
1255     return GST_FLOW_ERROR;
1256   }
1257
1258   if (!gst_video_frame_map (&vframe, &self->info, buffer, GST_MAP_READ)) {
1259     GST_ERROR_OBJECT (self, "Failed to map video frame");
1260     flow_ret = GST_FLOW_ERROR;
1261     goto out;
1262   }
1263
1264   frame->GetBytes ((void **) &outdata);
1265   indata = (guint8 *) GST_VIDEO_FRAME_PLANE_DATA (&vframe, 0);
1266   stride =
1267       MIN (GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, 0), frame->GetRowBytes ());
1268   for (i = 0; i < self->info.height; i++) {
1269     memcpy (outdata, indata, stride);
1270     indata += GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, 0);
1271     outdata += frame->GetRowBytes ();
1272   }
1273   gst_video_frame_unmap (&vframe);
1274
1275   tc_meta = gst_buffer_get_video_time_code_meta (buffer);
1276   if (tc_meta) {
1277     BMDTimecodeFlags bflags = (BMDTimecodeFlags) 0;
1278     gchar *tc_str;
1279
1280     if (((GstVideoTimeCodeFlags) (tc_meta->tc.
1281                 config.flags)) & GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME)
1282       bflags = (BMDTimecodeFlags) (bflags | bmdTimecodeIsDropFrame);
1283     else
1284       bflags = (BMDTimecodeFlags) (bflags | bmdTimecodeFlagDefault);
1285     if (tc_meta->tc.field_count == 2)
1286       bflags = (BMDTimecodeFlags) (bflags | bmdTimecodeFieldMark);
1287
1288     tc_str = gst_video_time_code_to_string (&tc_meta->tc);
1289     ret = frame->SetTimecodeFromComponents (self->timecode_format,
1290         (uint8_t) tc_meta->tc.hours,
1291         (uint8_t) tc_meta->tc.minutes,
1292         (uint8_t) tc_meta->tc.seconds, (uint8_t) tc_meta->tc.frames, bflags);
1293     if (ret != S_OK) {
1294       GST_ERROR_OBJECT (self,
1295           "Failed to set timecode %s to video frame: 0x%08lx", tc_str,
1296           (unsigned long) ret);
1297       flow_ret = GST_FLOW_ERROR;
1298       g_free (tc_str);
1299       goto out;
1300     }
1301     GST_DEBUG_OBJECT (self, "Set frame timecode to %s", tc_str);
1302     g_free (tc_str);
1303   }
1304
1305   write_vbi (self, buffer, format, frame, tc_meta);
1306
1307   gst_decklink_video_sink_convert_to_internal_clock (self, &running_time,
1308       &running_time_duration);
1309
1310   GST_LOG_OBJECT (self, "Scheduling video frame %p at %" GST_TIME_FORMAT
1311       " with duration %" GST_TIME_FORMAT, frame, GST_TIME_ARGS (running_time),
1312       GST_TIME_ARGS (running_time_duration));
1313
1314   ret = self->output->output->ScheduleVideoFrame (frame,
1315       running_time, running_time_duration, GST_SECOND);
1316   if (ret != S_OK) {
1317     GST_ELEMENT_ERROR (self, STREAM, FAILED,
1318         (NULL), ("Failed to schedule frame: 0x%08lx", (unsigned long) ret));
1319     flow_ret = GST_FLOW_ERROR;
1320     goto out;
1321   }
1322
1323   flow_ret = GST_FLOW_OK;
1324
1325 out:
1326
1327   frame->Release ();
1328
1329   return flow_ret;
1330 }
1331
1332 static gboolean
1333 gst_decklink_video_sink_open (GstBaseSink * bsink)
1334 {
1335   GstDecklinkVideoSink *self = GST_DECKLINK_VIDEO_SINK_CAST (bsink);
1336   const GstDecklinkMode *mode;
1337
1338   GST_DEBUG_OBJECT (self, "Starting");
1339
1340   self->output =
1341       gst_decklink_acquire_nth_output (self->device_number,
1342       GST_ELEMENT_CAST (self), FALSE);
1343   if (!self->output) {
1344     GST_ERROR_OBJECT (self, "Failed to acquire output");
1345     return FALSE;
1346   }
1347
1348   g_object_notify (G_OBJECT (self), "hw-serial-number");
1349
1350   mode = gst_decklink_get_mode (self->mode);
1351   g_assert (mode != NULL);
1352
1353   g_mutex_lock (&self->output->lock);
1354   self->output->mode = mode;
1355   self->output->start_scheduled_playback =
1356       gst_decklink_video_sink_start_scheduled_playback;
1357   self->output->clock_start_time = GST_CLOCK_TIME_NONE;
1358   self->output->clock_epoch += self->output->clock_last_time;
1359   self->output->clock_last_time = 0;
1360   self->output->clock_offset = 0;
1361   GST_OBJECT_LOCK (self);
1362   self->internal_base_time = GST_CLOCK_TIME_NONE;
1363   self->external_base_time = GST_CLOCK_TIME_NONE;
1364   GST_OBJECT_UNLOCK (self);
1365   g_mutex_unlock (&self->output->lock);
1366
1367   return TRUE;
1368 }
1369
1370 static gboolean
1371 gst_decklink_video_sink_close (GstBaseSink * bsink)
1372 {
1373   GstDecklinkVideoSink *self = GST_DECKLINK_VIDEO_SINK_CAST (bsink);
1374
1375   GST_DEBUG_OBJECT (self, "Closing");
1376
1377   if (self->output) {
1378     g_mutex_lock (&self->output->lock);
1379     self->output->mode = NULL;
1380     self->output->video_enabled = FALSE;
1381     if (self->output->start_scheduled_playback && self->output->videosink)
1382       self->output->start_scheduled_playback (self->output->videosink);
1383     g_mutex_unlock (&self->output->lock);
1384
1385     self->output->output->DisableVideoOutput ();
1386     gst_decklink_release_nth_output (self->device_number,
1387         GST_ELEMENT_CAST (self), FALSE);
1388     self->output = NULL;
1389   }
1390
1391   return TRUE;
1392 }
1393
1394 static gboolean
1395 gst_decklink_video_sink_stop (GstDecklinkVideoSink * self)
1396 {
1397   GST_DEBUG_OBJECT (self, "Stopping");
1398
1399   if (self->output && self->output->video_enabled) {
1400     g_mutex_lock (&self->output->lock);
1401     self->output->video_enabled = FALSE;
1402     g_mutex_unlock (&self->output->lock);
1403
1404     self->output->output->DisableVideoOutput ();
1405     self->output->output->SetScheduledFrameCompletionCallback (NULL);
1406   }
1407
1408   if (self->vbiencoder) {
1409     gst_video_vbi_encoder_free (self->vbiencoder);
1410     self->vbiencoder = NULL;
1411     self->anc_vformat = GST_VIDEO_FORMAT_UNKNOWN;
1412   }
1413
1414   return TRUE;
1415 }
1416
1417 static void
1418 _wait_for_stop_notify (GstDecklinkVideoSink * self)
1419 {
1420   bool active = false;
1421
1422   self->output->output->IsScheduledPlaybackRunning (&active);
1423   while (active) {
1424     /* cause sometimes decklink stops without notifying us... */
1425     guint64 wait_time = g_get_monotonic_time () + G_TIME_SPAN_SECOND;
1426     if (!g_cond_wait_until (&self->output->cond, &self->output->lock,
1427             wait_time))
1428       GST_WARNING_OBJECT (self, "Failed to wait for stop notification");
1429     self->output->output->IsScheduledPlaybackRunning (&active);
1430   }
1431 }
1432
1433 static void
1434 gst_decklink_video_sink_start_scheduled_playback (GstElement * element)
1435 {
1436   GstDecklinkVideoSink *self = GST_DECKLINK_VIDEO_SINK_CAST (element);
1437   GstClockTime start_time;
1438   HRESULT res;
1439   bool active;
1440
1441   // Check if we're already started
1442   if (self->output->started) {
1443     GST_DEBUG_OBJECT (self, "Already started");
1444     return;
1445   }
1446   // Check if we're ready to start:
1447   // we need video and audio enabled, if there is audio
1448   // and both of the two elements need to be set to PLAYING already
1449   if (!self->output->video_enabled) {
1450     GST_DEBUG_OBJECT (self,
1451         "Not starting scheduled playback yet: video not enabled yet!");
1452     return;
1453   }
1454
1455   if (self->output->audiosink && !self->output->audio_enabled) {
1456     GST_DEBUG_OBJECT (self,
1457         "Not starting scheduled playback yet: "
1458         "have audio but not enabled yet!");
1459     return;
1460   }
1461
1462   if ((GST_STATE (self) < GST_STATE_PAUSED
1463           && GST_STATE_PENDING (self) < GST_STATE_PAUSED)
1464       || (self->output->audiosink &&
1465           GST_STATE (self->output->audiosink) < GST_STATE_PAUSED
1466           && GST_STATE_PENDING (self->output->audiosink) < GST_STATE_PAUSED)) {
1467     GST_DEBUG_OBJECT (self,
1468         "Not starting scheduled playback yet: "
1469         "Elements are not set to PAUSED yet");
1470     return;
1471   }
1472   // Need to unlock to get the clock time
1473   g_mutex_unlock (&self->output->lock);
1474
1475   start_time = gst_clock_get_internal_time (self->output->clock);
1476
1477   g_mutex_lock (&self->output->lock);
1478   // Check if someone else started in the meantime
1479   if (self->output->started) {
1480     return;
1481   }
1482
1483   active = false;
1484   self->output->output->IsScheduledPlaybackRunning (&active);
1485   if (active) {
1486     GST_DEBUG_OBJECT (self, "Stopping scheduled playback");
1487
1488     self->output->started = FALSE;
1489
1490     res = self->output->output->StopScheduledPlayback (0, 0, 0);
1491     if (res != S_OK) {
1492       GST_ELEMENT_ERROR (self, STREAM, FAILED,
1493           (NULL), ("Failed to stop scheduled playback: 0x%08lx",
1494               (unsigned long) res));
1495       return;
1496     }
1497     // Wait until scheduled playback actually stopped
1498     _wait_for_stop_notify (self);
1499   }
1500
1501   GST_INFO_OBJECT (self,
1502       "Starting scheduled playback at %" GST_TIME_FORMAT,
1503       GST_TIME_ARGS (start_time));
1504
1505   res =
1506       self->output->output->StartScheduledPlayback (start_time,
1507       GST_SECOND, 1.0);
1508   if (res != S_OK) {
1509     GST_ELEMENT_ERROR (self, STREAM, FAILED,
1510         (NULL), ("Failed to start scheduled playback: 0x%08lx",
1511             (unsigned long) res));
1512     return;
1513   }
1514
1515   self->output->started = TRUE;
1516 }
1517
1518 static GstStateChangeReturn
1519 gst_decklink_video_sink_stop_scheduled_playback (GstDecklinkVideoSink * self)
1520 {
1521   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1522   GstClockTime start_time;
1523   HRESULT res;
1524
1525   if (!self->output->started)
1526     return ret;
1527
1528   start_time = gst_clock_get_internal_time (self->output->clock);
1529
1530   GST_INFO_OBJECT (self,
1531       "Stopping scheduled playback at %" GST_TIME_FORMAT,
1532       GST_TIME_ARGS (start_time));
1533
1534   g_mutex_lock (&self->output->lock);
1535   self->output->started = FALSE;
1536   res = self->output->output->StopScheduledPlayback (start_time, 0, GST_SECOND);
1537   if (res != S_OK) {
1538     GST_ELEMENT_ERROR (self, STREAM, FAILED,
1539         (NULL), ("Failed to stop scheduled playback: 0x%08lx", (unsigned long)
1540             res));
1541     ret = GST_STATE_CHANGE_FAILURE;
1542   } else {
1543
1544     // Wait until scheduled playback actually stopped
1545     _wait_for_stop_notify (self);
1546   }
1547   g_mutex_unlock (&self->output->lock);
1548   GST_OBJECT_LOCK (self);
1549   self->internal_base_time = GST_CLOCK_TIME_NONE;
1550   self->external_base_time = GST_CLOCK_TIME_NONE;
1551   GST_OBJECT_UNLOCK (self);
1552
1553   return ret;
1554 }
1555
1556 static GstStateChangeReturn
1557 gst_decklink_video_sink_change_state (GstElement * element,
1558     GstStateChange transition)
1559 {
1560   GstDecklinkVideoSink *self = GST_DECKLINK_VIDEO_SINK_CAST (element);
1561   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1562
1563   GST_DEBUG_OBJECT (self, "changing state: %s => %s",
1564       gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
1565       gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
1566
1567   switch (transition) {
1568     case GST_STATE_CHANGE_READY_TO_PAUSED:
1569       self->vbiencoder = NULL;
1570       self->anc_vformat = GST_VIDEO_FORMAT_UNKNOWN;
1571       self->cdp_hdr_sequence_cntr = 0;
1572
1573       g_mutex_lock (&self->output->lock);
1574       self->output->clock_epoch += self->output->clock_last_time;
1575       self->output->clock_last_time = 0;
1576       self->output->clock_offset = 0;
1577       g_mutex_unlock (&self->output->lock);
1578       gst_element_post_message (element,
1579           gst_message_new_clock_provide (GST_OBJECT_CAST (element),
1580               self->output->clock, TRUE));
1581       g_mutex_lock (&self->output->lock);
1582       if (self->output->start_scheduled_playback)
1583         self->output->start_scheduled_playback (self->output->videosink);
1584       g_mutex_unlock (&self->output->lock);
1585       break;
1586     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:{
1587       GstClock *clock;
1588
1589       clock = gst_element_get_clock (GST_ELEMENT_CAST (self));
1590       if (clock) {
1591         if (clock != self->output->clock) {
1592           gst_clock_set_master (self->output->clock, clock);
1593         }
1594
1595         GST_OBJECT_LOCK (self);
1596         if (self->external_base_time == GST_CLOCK_TIME_NONE
1597             || self->internal_base_time == GST_CLOCK_TIME_NONE) {
1598           self->external_base_time = gst_clock_get_internal_time (clock);
1599           self->internal_base_time =
1600               gst_clock_get_internal_time (self->output->clock);
1601           self->internal_time_offset = self->internal_base_time;
1602         } else if (GST_CLOCK_TIME_IS_VALID (self->internal_pause_time)) {
1603           self->internal_time_offset +=
1604                   gst_clock_get_internal_time (self->output->clock) - self->internal_pause_time;
1605         }
1606
1607         GST_INFO_OBJECT (self, "clock has been set to %" GST_PTR_FORMAT
1608             ", updated base times - internal: %" GST_TIME_FORMAT
1609             " external: %" GST_TIME_FORMAT " internal offset %"
1610             GST_TIME_FORMAT, clock,
1611             GST_TIME_ARGS (self->internal_base_time),
1612             GST_TIME_ARGS (self->external_base_time),
1613             GST_TIME_ARGS (self->internal_time_offset));
1614         GST_OBJECT_UNLOCK (self);
1615
1616         gst_object_unref (clock);
1617       } else {
1618         GST_ELEMENT_ERROR (self, STREAM, FAILED,
1619             (NULL), ("Need a clock to go to PLAYING"));
1620         ret = GST_STATE_CHANGE_FAILURE;
1621       }
1622       break;
1623     }
1624     case GST_STATE_CHANGE_PAUSED_TO_READY:
1625       if (gst_decklink_video_sink_stop_scheduled_playback (self) ==
1626           GST_STATE_CHANGE_FAILURE)
1627         ret = GST_STATE_CHANGE_FAILURE;
1628       break;
1629     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1630       break;
1631     default:
1632       break;
1633   }
1634
1635   if (ret == GST_STATE_CHANGE_FAILURE)
1636     return ret;
1637   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1638   if (ret == GST_STATE_CHANGE_FAILURE)
1639     return ret;
1640
1641   switch (transition) {
1642     case GST_STATE_CHANGE_PAUSED_TO_READY:{
1643       gst_element_post_message (element,
1644           gst_message_new_clock_lost (GST_OBJECT_CAST (element),
1645               self->output->clock));
1646       gst_clock_set_master (self->output->clock, NULL);
1647       // Reset calibration to make the clock reusable next time we use it
1648       gst_clock_set_calibration (self->output->clock, 0, 0, 1, 1);
1649       g_mutex_lock (&self->output->lock);
1650       self->output->clock_epoch += self->output->clock_last_time;
1651       self->output->clock_last_time = 0;
1652       self->output->clock_offset = 0;
1653       g_mutex_unlock (&self->output->lock);
1654       gst_decklink_video_sink_stop (self);
1655       GST_OBJECT_LOCK (self);
1656       self->internal_base_time = GST_CLOCK_TIME_NONE;
1657       self->external_base_time = GST_CLOCK_TIME_NONE;
1658       self->internal_pause_time = GST_CLOCK_TIME_NONE;
1659       GST_OBJECT_UNLOCK (self);
1660       break;
1661     }
1662     case GST_STATE_CHANGE_READY_TO_PAUSED:{
1663       break;
1664     }
1665     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1666       break;
1667     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1668       self->internal_pause_time = gst_clock_get_internal_time (self->output->clock);
1669       break;
1670     default:
1671       break;
1672   }
1673
1674   return ret;
1675 }
1676
1677 static gboolean
1678 gst_decklink_video_sink_event (GstBaseSink * bsink, GstEvent * event)
1679 {
1680   GstDecklinkVideoSink *self = GST_DECKLINK_VIDEO_SINK_CAST (bsink);
1681
1682   switch (GST_EVENT_TYPE (event)) {
1683     case GST_EVENT_FLUSH_START:
1684     {
1685       break;
1686     }
1687     case GST_EVENT_FLUSH_STOP:
1688     {
1689       gboolean reset_time;
1690
1691       gst_event_parse_flush_stop (event, &reset_time);
1692       if (reset_time) {
1693         GST_OBJECT_LOCK (self);
1694         /* force a recalculation of clock base times */
1695         self->external_base_time = GST_CLOCK_TIME_NONE;
1696         self->internal_base_time = GST_CLOCK_TIME_NONE;
1697         GST_OBJECT_UNLOCK (self);
1698       }
1699       break;
1700     }
1701     default:
1702       break;
1703   }
1704
1705   return GST_BASE_SINK_CLASS (parent_class)->event (bsink, event);
1706 }
1707
1708 static GstClock *
1709 gst_decklink_video_sink_provide_clock (GstElement * element)
1710 {
1711   GstDecklinkVideoSink *self = GST_DECKLINK_VIDEO_SINK_CAST (element);
1712
1713   if (!self->output)
1714     return NULL;
1715
1716   return GST_CLOCK_CAST (gst_object_ref (self->output->clock));
1717 }
1718
1719 static gboolean
1720 gst_decklink_video_sink_propose_allocation (GstBaseSink * bsink,
1721     GstQuery * query)
1722 {
1723   GstCaps *caps;
1724   GstVideoInfo info;
1725   GstBufferPool *pool;
1726   guint size;
1727
1728   gst_query_parse_allocation (query, &caps, NULL);
1729
1730   if (caps == NULL)
1731     return FALSE;
1732
1733   if (!gst_video_info_from_caps (&info, caps))
1734     return FALSE;
1735
1736   size = GST_VIDEO_INFO_SIZE (&info);
1737
1738   if (gst_query_get_n_allocation_pools (query) == 0) {
1739     GstStructure *structure;
1740     GstAllocator *allocator = NULL;
1741     GstAllocationParams params = { (GstMemoryFlags) 0, 15, 0, 0 };
1742
1743     if (gst_query_get_n_allocation_params (query) > 0)
1744       gst_query_parse_nth_allocation_param (query, 0, &allocator, &params);
1745     else
1746       gst_query_add_allocation_param (query, allocator, &params);
1747
1748     pool = gst_video_buffer_pool_new ();
1749
1750     structure = gst_buffer_pool_get_config (pool);
1751     gst_buffer_pool_config_set_params (structure, caps, size, 0, 0);
1752     gst_buffer_pool_config_set_allocator (structure, allocator, &params);
1753
1754     if (allocator)
1755       gst_object_unref (allocator);
1756
1757     if (!gst_buffer_pool_set_config (pool, structure))
1758       goto config_failed;
1759
1760     gst_query_add_allocation_pool (query, pool, size, 0, 0);
1761     gst_object_unref (pool);
1762     gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
1763   }
1764
1765   return TRUE;
1766   // ERRORS
1767 config_failed:
1768   {
1769     GST_ERROR_OBJECT (bsink, "failed to set config");
1770     gst_object_unref (pool);
1771     return FALSE;
1772   }
1773 }