mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-23 16:50:47 +00:00
decklink: Add initial version of audio and video sources
This commit is contained in:
parent
57d46fe9e1
commit
6d931a0c0e
8 changed files with 1301 additions and 4 deletions
|
@ -26,7 +26,9 @@ libgstdecklink_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS)
|
||||||
libgstdecklink_la_SOURCES = \
|
libgstdecklink_la_SOURCES = \
|
||||||
gstdecklink.cpp \
|
gstdecklink.cpp \
|
||||||
gstdecklinkaudiosink.cpp \
|
gstdecklinkaudiosink.cpp \
|
||||||
gstdecklinkvideosink.cpp
|
gstdecklinkvideosink.cpp \
|
||||||
|
gstdecklinkaudiosrc.cpp \
|
||||||
|
gstdecklinkvideosrc.cpp
|
||||||
|
|
||||||
if DECKLINK_OSX
|
if DECKLINK_OSX
|
||||||
libgstdecklink_la_SOURCES += \
|
libgstdecklink_la_SOURCES += \
|
||||||
|
@ -40,6 +42,8 @@ noinst_HEADERS = \
|
||||||
gstdecklink.h \
|
gstdecklink.h \
|
||||||
gstdecklinkaudiosink.h \
|
gstdecklinkaudiosink.h \
|
||||||
gstdecklinkvideosink.h \
|
gstdecklinkvideosink.h \
|
||||||
|
gstdecklinkaudiosrc.h \
|
||||||
|
gstdecklinkvideosrc.h \
|
||||||
linux/DeckLinkAPIConfiguration.h \
|
linux/DeckLinkAPIConfiguration.h \
|
||||||
linux/DeckLinkAPIDeckControl.h \
|
linux/DeckLinkAPIDeckControl.h \
|
||||||
linux/DeckLinkAPIDiscovery.h \
|
linux/DeckLinkAPIDiscovery.h \
|
||||||
|
|
|
@ -26,6 +26,8 @@
|
||||||
#include "gstdecklink.h"
|
#include "gstdecklink.h"
|
||||||
#include "gstdecklinkaudiosink.h"
|
#include "gstdecklinkaudiosink.h"
|
||||||
#include "gstdecklinkvideosink.h"
|
#include "gstdecklinkvideosink.h"
|
||||||
|
#include "gstdecklinkaudiosrc.h"
|
||||||
|
#include "gstdecklinkvideosrc.h"
|
||||||
|
|
||||||
GST_DEBUG_CATEGORY_STATIC (gst_decklink_debug);
|
GST_DEBUG_CATEGORY_STATIC (gst_decklink_debug);
|
||||||
#define GST_CAT_DEFAULT gst_decklink_debug
|
#define GST_CAT_DEFAULT gst_decklink_debug
|
||||||
|
@ -241,6 +243,91 @@ struct _Device
|
||||||
GstDecklinkInput input;
|
GstDecklinkInput input;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class GStreamerDecklinkInputCallback:public IDeckLinkInputCallback
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
GstDecklinkInput * m_input;
|
||||||
|
GMutex m_mutex;
|
||||||
|
gint m_refcount;
|
||||||
|
public:
|
||||||
|
GStreamerDecklinkInputCallback (GstDecklinkInput * input)
|
||||||
|
: IDeckLinkInputCallback ()
|
||||||
|
{
|
||||||
|
m_input = input;
|
||||||
|
g_mutex_init (&m_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~ GStreamerDecklinkInputCallback ()
|
||||||
|
{
|
||||||
|
g_mutex_clear (&m_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual HRESULT STDMETHODCALLTYPE QueryInterface (REFIID, LPVOID *)
|
||||||
|
{
|
||||||
|
return E_NOINTERFACE;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ULONG STDMETHODCALLTYPE AddRef (void)
|
||||||
|
{
|
||||||
|
ULONG ret;
|
||||||
|
|
||||||
|
g_mutex_lock (&m_mutex);
|
||||||
|
m_refcount++;
|
||||||
|
ret = m_refcount;
|
||||||
|
g_mutex_unlock (&m_mutex);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ULONG STDMETHODCALLTYPE Release (void)
|
||||||
|
{
|
||||||
|
ULONG ret;
|
||||||
|
|
||||||
|
g_mutex_lock (&m_mutex);
|
||||||
|
m_refcount--;
|
||||||
|
ret = m_refcount;
|
||||||
|
g_mutex_unlock (&m_mutex);
|
||||||
|
|
||||||
|
|
||||||
|
if (ret == 0) {
|
||||||
|
delete this;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual HRESULT STDMETHODCALLTYPE
|
||||||
|
VideoInputFormatChanged (BMDVideoInputFormatChangedEvents,
|
||||||
|
IDeckLinkDisplayMode *, BMDDetectedVideoInputFormatFlags)
|
||||||
|
{
|
||||||
|
GST_FIXME ("Video input format change not supported yet");
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual HRESULT STDMETHODCALLTYPE
|
||||||
|
VideoInputFrameArrived (IDeckLinkVideoInputFrame * video_frame,
|
||||||
|
IDeckLinkAudioInputPacket * audio_packet)
|
||||||
|
{
|
||||||
|
GstClockTime clock_time = gst_clock_get_time (m_input->clock);
|
||||||
|
|
||||||
|
g_mutex_lock (&m_input->lock);
|
||||||
|
if (m_input->got_video_frame) {
|
||||||
|
GstClockTime capture_time = clock_time -
|
||||||
|
gst_element_get_base_time (m_input->videosrc);
|
||||||
|
m_input->got_video_frame (m_input->videosrc, video_frame, capture_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_input->got_audio_packet) {
|
||||||
|
GstClockTime capture_time = clock_time -
|
||||||
|
gst_element_get_base_time (m_input->audiosrc);
|
||||||
|
m_input->got_audio_packet (m_input->audiosrc, audio_packet, capture_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_mutex_unlock (&m_input->lock);
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
static GOnce devices_once = G_ONCE_INIT;
|
static GOnce devices_once = G_ONCE_INIT;
|
||||||
static int n_devices;
|
static int n_devices;
|
||||||
static Device devices[10];
|
static Device devices[10];
|
||||||
|
@ -273,6 +360,9 @@ init_devices (gpointer data)
|
||||||
"GstDecklinkInputClock", NULL));
|
"GstDecklinkInputClock", NULL));
|
||||||
GST_DECKLINK_CLOCK_CAST (devices[i].input.clock)->input =
|
GST_DECKLINK_CLOCK_CAST (devices[i].input.clock)->input =
|
||||||
devices[i].input.input;
|
devices[i].input.input;
|
||||||
|
devices[i].input.
|
||||||
|
input->SetCallback (new GStreamerDecklinkInputCallback (&devices[i].
|
||||||
|
input));
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = decklink->QueryInterface (IID_IDeckLinkOutput,
|
ret = decklink->QueryInterface (IID_IDeckLinkOutput,
|
||||||
|
@ -393,6 +483,62 @@ gst_decklink_output_get_audio_clock (GstDecklinkOutput * output)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GstDecklinkInput *
|
||||||
|
gst_decklink_acquire_nth_input (gint n, GstElement * src, gboolean is_audio)
|
||||||
|
{
|
||||||
|
GstDecklinkInput *input;
|
||||||
|
|
||||||
|
g_once (&devices_once, init_devices, NULL);
|
||||||
|
|
||||||
|
if (n >= n_devices)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
input = &devices[n].input;
|
||||||
|
if (!input->input) {
|
||||||
|
GST_ERROR ("Device %d has no input", n);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_mutex_lock (&input->lock);
|
||||||
|
if (is_audio && !input->audiosrc) {
|
||||||
|
input->audiosrc = GST_ELEMENT_CAST (gst_object_ref (src));
|
||||||
|
g_mutex_unlock (&input->lock);
|
||||||
|
return input;
|
||||||
|
} else if (!input->videosrc) {
|
||||||
|
input->videosrc = GST_ELEMENT_CAST (gst_object_ref (src));
|
||||||
|
g_mutex_unlock (&input->lock);
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
g_mutex_unlock (&input->lock);
|
||||||
|
|
||||||
|
GST_ERROR ("Input device %d (audio: %d) in use already", n, is_audio);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
gst_decklink_release_nth_input (gint n, GstElement * src, gboolean is_audio)
|
||||||
|
{
|
||||||
|
GstDecklinkInput *input;
|
||||||
|
|
||||||
|
if (n >= n_devices)
|
||||||
|
return;
|
||||||
|
|
||||||
|
input = &devices[n].input;
|
||||||
|
g_assert (input->input);
|
||||||
|
|
||||||
|
g_mutex_lock (&input->lock);
|
||||||
|
if (is_audio) {
|
||||||
|
g_assert (input->audiosrc == src);
|
||||||
|
gst_object_unref (src);
|
||||||
|
input->audiosrc = NULL;
|
||||||
|
} else {
|
||||||
|
g_assert (input->videosrc == src);
|
||||||
|
gst_object_unref (src);
|
||||||
|
input->videosrc = NULL;
|
||||||
|
}
|
||||||
|
g_mutex_unlock (&input->lock);
|
||||||
|
}
|
||||||
|
|
||||||
G_DEFINE_TYPE (GstDecklinkClock, gst_decklink_clock, GST_TYPE_SYSTEM_CLOCK);
|
G_DEFINE_TYPE (GstDecklinkClock, gst_decklink_clock, GST_TYPE_SYSTEM_CLOCK);
|
||||||
|
|
||||||
static void gst_decklink_clock_class_init (GstDecklinkClockClass * klass);
|
static void gst_decklink_clock_class_init (GstDecklinkClockClass * klass);
|
||||||
|
@ -466,6 +612,10 @@ plugin_init (GstPlugin * plugin)
|
||||||
GST_TYPE_DECKLINK_AUDIO_SINK);
|
GST_TYPE_DECKLINK_AUDIO_SINK);
|
||||||
gst_element_register (plugin, "decklinkvideosink", GST_RANK_NONE,
|
gst_element_register (plugin, "decklinkvideosink", GST_RANK_NONE,
|
||||||
GST_TYPE_DECKLINK_VIDEO_SINK);
|
GST_TYPE_DECKLINK_VIDEO_SINK);
|
||||||
|
gst_element_register (plugin, "decklinkaudiosrc", GST_RANK_NONE,
|
||||||
|
GST_TYPE_DECKLINK_AUDIO_SRC);
|
||||||
|
gst_element_register (plugin, "decklinkvideosrc", GST_RANK_NONE,
|
||||||
|
GST_TYPE_DECKLINK_VIDEO_SRC);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -139,8 +139,11 @@ struct _GstDecklinkInput {
|
||||||
/* Everything below protected by mutex */
|
/* Everything below protected by mutex */
|
||||||
GMutex lock;
|
GMutex lock;
|
||||||
|
|
||||||
|
/* Set by the video source */
|
||||||
|
void (*got_video_frame) (GstElement *videosrc, IDeckLinkVideoInputFrame * frame, GstClockTime capture_time);
|
||||||
|
|
||||||
/* Set by the audio source */
|
/* Set by the audio source */
|
||||||
GstClock *audio_clock;
|
void (*got_audio_packet) (GstElement *videosrc, IDeckLinkAudioInputPacket * packet, GstClockTime capture_time);
|
||||||
|
|
||||||
/* <private> */
|
/* <private> */
|
||||||
GstElement *audiosrc;
|
GstElement *audiosrc;
|
||||||
|
@ -153,4 +156,7 @@ void gst_decklink_release_nth_output (gint n, GstElement * sink,
|
||||||
void gst_decklink_output_set_audio_clock (GstDecklinkOutput * output, GstClock * clock);
|
void gst_decklink_output_set_audio_clock (GstDecklinkOutput * output, GstClock * clock);
|
||||||
GstClock * gst_decklink_output_get_audio_clock (GstDecklinkOutput * output);
|
GstClock * gst_decklink_output_get_audio_clock (GstDecklinkOutput * output);
|
||||||
|
|
||||||
|
GstDecklinkInput * gst_decklink_acquire_nth_input (gint n, GstElement * src, gboolean is_audio);
|
||||||
|
void gst_decklink_release_nth_input (gint n, GstElement * src, gboolean is_audio);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -285,7 +285,6 @@ in_same_pipeline (GstElement * a, GstElement * b)
|
||||||
gst_object_unref (root);
|
gst_object_unref (root);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
|
@ -438,7 +437,6 @@ gst_decklink_audio_sink_ringbuffer_close_device (GstAudioRingBuffer * rb)
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
PROP_0,
|
PROP_0,
|
||||||
PROP_MODE,
|
|
||||||
PROP_DEVICE_NUMBER
|
PROP_DEVICE_NUMBER
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
528
sys/decklink/gstdecklinkaudiosrc.cpp
Normal file
528
sys/decklink/gstdecklinkaudiosrc.cpp
Normal file
|
@ -0,0 +1,528 @@
|
||||||
|
/* GStreamer
|
||||||
|
* Copyright (C) 2011 David Schleef <ds@entropywave.com>
|
||||||
|
* Copyright (C) 2014 Sebastian Dröge <sebastian@centricular.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 51 Franklin Street, Suite 500,
|
||||||
|
* Boston, MA 02110-1335, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "gstdecklinkaudiosrc.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
GST_DEBUG_CATEGORY_STATIC (gst_decklink_audio_src_debug);
|
||||||
|
#define GST_CAT_DEFAULT gst_decklink_audio_src_debug
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
PROP_0,
|
||||||
|
PROP_MODE,
|
||||||
|
PROP_DEVICE_NUMBER
|
||||||
|
};
|
||||||
|
|
||||||
|
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("src",
|
||||||
|
GST_PAD_SRC,
|
||||||
|
GST_PAD_ALWAYS,
|
||||||
|
GST_STATIC_CAPS
|
||||||
|
("audio/x-raw, format={S16LE,S32LE}, channels=2, rate=48000, "
|
||||||
|
"layout=interleaved")
|
||||||
|
);
|
||||||
|
|
||||||
|
static void gst_decklink_audio_src_set_property (GObject * object,
|
||||||
|
guint property_id, const GValue * value, GParamSpec * pspec);
|
||||||
|
static void gst_decklink_audio_src_get_property (GObject * object,
|
||||||
|
guint property_id, GValue * value, GParamSpec * pspec);
|
||||||
|
static void gst_decklink_audio_src_finalize (GObject * object);
|
||||||
|
|
||||||
|
static GstStateChangeReturn
|
||||||
|
gst_decklink_audio_src_change_state (GstElement * element,
|
||||||
|
GstStateChange transition);
|
||||||
|
static GstClock *gst_decklink_audio_src_provide_clock (GstElement * element);
|
||||||
|
|
||||||
|
static gboolean gst_decklink_audio_src_set_caps (GstBaseSrc * bsrc,
|
||||||
|
GstCaps * caps);
|
||||||
|
static GstCaps *gst_decklink_audio_src_get_caps (GstBaseSrc * bsrc,
|
||||||
|
GstCaps * filter);
|
||||||
|
static gboolean gst_decklink_audio_src_unlock (GstBaseSrc * bsrc);
|
||||||
|
static gboolean gst_decklink_audio_src_unlock_stop (GstBaseSrc * bsrc);
|
||||||
|
|
||||||
|
static GstFlowReturn gst_decklink_audio_src_create (GstPushSrc * psrc,
|
||||||
|
GstBuffer ** buffer);
|
||||||
|
|
||||||
|
static gboolean gst_decklink_audio_src_open (GstDecklinkAudioSrc * self);
|
||||||
|
static gboolean gst_decklink_audio_src_close (GstDecklinkAudioSrc * self);
|
||||||
|
|
||||||
|
#define parent_class gst_decklink_audio_src_parent_class
|
||||||
|
G_DEFINE_TYPE (GstDecklinkAudioSrc, gst_decklink_audio_src, GST_TYPE_PUSH_SRC);
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_decklink_audio_src_class_init (GstDecklinkAudioSrcClass * klass)
|
||||||
|
{
|
||||||
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||||
|
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
|
||||||
|
GstBaseSrcClass *basesrc_class = GST_BASE_SRC_CLASS (klass);
|
||||||
|
GstPushSrcClass *pushsrc_class = GST_PUSH_SRC_CLASS (klass);
|
||||||
|
|
||||||
|
gobject_class->set_property = gst_decklink_audio_src_set_property;
|
||||||
|
gobject_class->get_property = gst_decklink_audio_src_get_property;
|
||||||
|
gobject_class->finalize = gst_decklink_audio_src_finalize;
|
||||||
|
|
||||||
|
element_class->change_state =
|
||||||
|
GST_DEBUG_FUNCPTR (gst_decklink_audio_src_change_state);
|
||||||
|
element_class->provide_clock =
|
||||||
|
GST_DEBUG_FUNCPTR (gst_decklink_audio_src_provide_clock);
|
||||||
|
|
||||||
|
basesrc_class->get_caps = GST_DEBUG_FUNCPTR (gst_decklink_audio_src_get_caps);
|
||||||
|
basesrc_class->set_caps = GST_DEBUG_FUNCPTR (gst_decklink_audio_src_set_caps);
|
||||||
|
basesrc_class->unlock = GST_DEBUG_FUNCPTR (gst_decklink_audio_src_unlock);
|
||||||
|
basesrc_class->unlock_stop =
|
||||||
|
GST_DEBUG_FUNCPTR (gst_decklink_audio_src_unlock_stop);
|
||||||
|
|
||||||
|
pushsrc_class->create = GST_DEBUG_FUNCPTR (gst_decklink_audio_src_create);
|
||||||
|
|
||||||
|
g_object_class_install_property (gobject_class, PROP_MODE,
|
||||||
|
g_param_spec_enum ("mode", "Playback Mode",
|
||||||
|
"Audio Mode to use for playback",
|
||||||
|
GST_TYPE_DECKLINK_MODE, GST_DECKLINK_MODE_NTSC,
|
||||||
|
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
|
||||||
|
G_PARAM_CONSTRUCT)));
|
||||||
|
|
||||||
|
g_object_class_install_property (gobject_class, PROP_DEVICE_NUMBER,
|
||||||
|
g_param_spec_int ("device-number", "Device number",
|
||||||
|
"Output device instance to use", 0, G_MAXINT, 0,
|
||||||
|
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
|
||||||
|
G_PARAM_CONSTRUCT)));
|
||||||
|
|
||||||
|
gst_element_class_add_pad_template (element_class,
|
||||||
|
gst_static_pad_template_get (&sink_template));
|
||||||
|
|
||||||
|
gst_element_class_set_static_metadata (element_class, "Decklink Audio Source",
|
||||||
|
"Audio/Src", "Decklink Source", "David Schleef <ds@entropywave.com>, "
|
||||||
|
"Sebastian Dröge <sebastian@centricular.com>");
|
||||||
|
|
||||||
|
GST_DEBUG_CATEGORY_INIT (gst_decklink_audio_src_debug, "decklinkaudiosrc",
|
||||||
|
0, "debug category for decklinkaudiosrc element");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_decklink_audio_src_init (GstDecklinkAudioSrc * self)
|
||||||
|
{
|
||||||
|
self->mode = GST_DECKLINK_MODE_NTSC;
|
||||||
|
self->device_number = 0;
|
||||||
|
|
||||||
|
gst_base_src_set_live (GST_BASE_SRC (self), TRUE);
|
||||||
|
gst_base_src_set_format (GST_BASE_SRC (self), GST_FORMAT_TIME);
|
||||||
|
|
||||||
|
g_mutex_init (&self->lock);
|
||||||
|
g_cond_init (&self->cond);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
gst_decklink_audio_src_set_property (GObject * object, guint property_id,
|
||||||
|
const GValue * value, GParamSpec * pspec)
|
||||||
|
{
|
||||||
|
GstDecklinkAudioSrc *self = GST_DECKLINK_AUDIO_SRC_CAST (object);
|
||||||
|
|
||||||
|
switch (property_id) {
|
||||||
|
case PROP_MODE:
|
||||||
|
self->mode = (GstDecklinkModeEnum) g_value_get_enum (value);
|
||||||
|
break;
|
||||||
|
case PROP_DEVICE_NUMBER:
|
||||||
|
self->device_number = g_value_get_int (value);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
gst_decklink_audio_src_get_property (GObject * object, guint property_id,
|
||||||
|
GValue * value, GParamSpec * pspec)
|
||||||
|
{
|
||||||
|
GstDecklinkAudioSrc *self = GST_DECKLINK_AUDIO_SRC_CAST (object);
|
||||||
|
|
||||||
|
switch (property_id) {
|
||||||
|
case PROP_MODE:
|
||||||
|
g_value_set_enum (value, self->mode);
|
||||||
|
break;
|
||||||
|
case PROP_DEVICE_NUMBER:
|
||||||
|
g_value_set_int (value, self->device_number);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
gst_decklink_audio_src_finalize (GObject * object)
|
||||||
|
{
|
||||||
|
GstDecklinkAudioSrc *self = GST_DECKLINK_AUDIO_SRC_CAST (object);
|
||||||
|
|
||||||
|
g_mutex_clear (&self->lock);
|
||||||
|
g_cond_clear (&self->cond);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_decklink_audio_src_set_caps (GstBaseSrc * bsrc, GstCaps * caps)
|
||||||
|
{
|
||||||
|
GstDecklinkAudioSrc *self = GST_DECKLINK_AUDIO_SRC_CAST (bsrc);
|
||||||
|
BMDAudioSampleType sample_depth;
|
||||||
|
GstCaps *current_caps;
|
||||||
|
HRESULT ret;
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (self, "Setting caps %" GST_PTR_FORMAT, caps);
|
||||||
|
|
||||||
|
if ((current_caps = gst_pad_get_current_caps (GST_BASE_SRC_PAD (bsrc)))) {
|
||||||
|
GST_DEBUG_OBJECT (self, "Pad already has caps %" GST_PTR_FORMAT, caps);
|
||||||
|
|
||||||
|
if (!gst_caps_is_equal (caps, current_caps)) {
|
||||||
|
GST_ERROR_OBJECT (self, "New caps are not equal to old caps");
|
||||||
|
gst_caps_unref (current_caps);
|
||||||
|
return FALSE;
|
||||||
|
} else {
|
||||||
|
gst_caps_unref (current_caps);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gst_audio_info_from_caps (&self->info, caps))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (self->info.finfo->format == GST_AUDIO_FORMAT_S16LE) {
|
||||||
|
sample_depth = bmdAudioSampleType16bitInteger;
|
||||||
|
} else {
|
||||||
|
sample_depth = bmdAudioSampleType32bitInteger;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = self->input->input->EnableAudioInput (bmdAudioSampleRate48kHz,
|
||||||
|
sample_depth, 2);
|
||||||
|
if (ret != S_OK) {
|
||||||
|
GST_WARNING_OBJECT (self, "Failed to enable audio input");
|
||||||
|
gst_decklink_release_nth_input (self->device_number,
|
||||||
|
GST_ELEMENT_CAST (self), FALSE);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstCaps *
|
||||||
|
gst_decklink_audio_src_get_caps (GstBaseSrc * bsrc, GstCaps * filter)
|
||||||
|
{
|
||||||
|
GstCaps *caps;
|
||||||
|
|
||||||
|
// We don't support renegotiation
|
||||||
|
caps = gst_pad_get_current_caps (GST_BASE_SRC_PAD (bsrc));
|
||||||
|
|
||||||
|
if (!caps)
|
||||||
|
caps = gst_pad_get_pad_template_caps (GST_BASE_SRC_PAD (bsrc));
|
||||||
|
|
||||||
|
if (filter) {
|
||||||
|
GstCaps *tmp =
|
||||||
|
gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
|
||||||
|
gst_caps_unref (caps);
|
||||||
|
caps = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
return caps;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_decklink_audio_src_got_packet (GstElement * element,
|
||||||
|
IDeckLinkAudioInputPacket * packet, GstClockTime capture_time)
|
||||||
|
{
|
||||||
|
GstDecklinkAudioSrc *self = GST_DECKLINK_AUDIO_SRC_CAST (element);
|
||||||
|
|
||||||
|
GST_LOG_OBJECT (self, "Got audio packet at %" GST_TIME_FORMAT,
|
||||||
|
GST_TIME_ARGS (capture_time));
|
||||||
|
|
||||||
|
g_mutex_lock (&self->lock);
|
||||||
|
if (!self->flushing) {
|
||||||
|
if (self->current_packet)
|
||||||
|
self->current_packet->Release ();
|
||||||
|
self->current_packet = packet;
|
||||||
|
packet->AddRef ();
|
||||||
|
self->current_packet_capture_time = capture_time;
|
||||||
|
g_cond_signal (&self->cond);
|
||||||
|
}
|
||||||
|
g_mutex_unlock (&self->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
IDeckLinkAudioInputPacket *packet;
|
||||||
|
IDeckLinkInput *input;
|
||||||
|
} AudioPacket;
|
||||||
|
|
||||||
|
static void
|
||||||
|
audio_packet_free (void *data)
|
||||||
|
{
|
||||||
|
AudioPacket *audio_packet = (AudioPacket *) data;
|
||||||
|
|
||||||
|
audio_packet->packet->Release ();
|
||||||
|
audio_packet->input->Release ();
|
||||||
|
g_free (audio_packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstFlowReturn
|
||||||
|
gst_decklink_audio_src_create (GstPushSrc * bsrc, GstBuffer ** buffer)
|
||||||
|
{
|
||||||
|
GstDecklinkAudioSrc *self = GST_DECKLINK_AUDIO_SRC_CAST (bsrc);
|
||||||
|
GstFlowReturn flow_ret = GST_FLOW_OK;
|
||||||
|
IDeckLinkAudioInputPacket *packet = NULL;
|
||||||
|
GstClockTime capture_time = GST_CLOCK_TIME_NONE;
|
||||||
|
const guint8 *data;
|
||||||
|
glong sample_count;
|
||||||
|
gsize data_size;
|
||||||
|
AudioPacket *ap;
|
||||||
|
|
||||||
|
g_mutex_lock (&self->lock);
|
||||||
|
while (!self->current_packet && !self->flushing) {
|
||||||
|
g_cond_wait (&self->cond, &self->lock);
|
||||||
|
}
|
||||||
|
packet = self->current_packet;
|
||||||
|
capture_time = self->current_packet_capture_time;
|
||||||
|
self->current_packet = NULL;
|
||||||
|
g_mutex_unlock (&self->lock);
|
||||||
|
|
||||||
|
if (self->flushing) {
|
||||||
|
if (packet)
|
||||||
|
packet->Release ();
|
||||||
|
return GST_FLOW_FLUSHING;
|
||||||
|
}
|
||||||
|
|
||||||
|
packet->GetBytes ((gpointer *) & data);
|
||||||
|
sample_count = packet->GetSampleFrameCount ();
|
||||||
|
data_size = self->info.bpf * sample_count;
|
||||||
|
|
||||||
|
ap = (AudioPacket *) g_malloc0 (sizeof (AudioPacket));
|
||||||
|
|
||||||
|
*buffer =
|
||||||
|
gst_buffer_new_wrapped_full ((GstMemoryFlags) GST_MEMORY_FLAG_READONLY,
|
||||||
|
(gpointer) data, data_size, 0, data_size, ap,
|
||||||
|
(GDestroyNotify) audio_packet_free);
|
||||||
|
|
||||||
|
ap->packet = packet;
|
||||||
|
ap->input = self->input->input;
|
||||||
|
ap->input->AddRef ();
|
||||||
|
|
||||||
|
// TODO: Jitter/discont handling
|
||||||
|
GST_BUFFER_TIMESTAMP (*buffer) = capture_time;
|
||||||
|
GST_BUFFER_DURATION (*buffer) =
|
||||||
|
gst_util_uint64_scale_int (sample_count, GST_SECOND, self->info.rate);
|
||||||
|
|
||||||
|
return flow_ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_decklink_audio_src_unlock (GstBaseSrc * bsrc)
|
||||||
|
{
|
||||||
|
GstDecklinkAudioSrc *self = GST_DECKLINK_AUDIO_SRC_CAST (bsrc);
|
||||||
|
|
||||||
|
g_mutex_lock (&self->lock);
|
||||||
|
self->flushing = TRUE;
|
||||||
|
g_cond_signal (&self->cond);
|
||||||
|
g_mutex_unlock (&self->lock);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_decklink_audio_src_unlock_stop (GstBaseSrc * bsrc)
|
||||||
|
{
|
||||||
|
GstDecklinkAudioSrc *self = GST_DECKLINK_AUDIO_SRC_CAST (bsrc);
|
||||||
|
|
||||||
|
g_mutex_lock (&self->lock);
|
||||||
|
self->flushing = FALSE;
|
||||||
|
g_mutex_unlock (&self->lock);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_decklink_audio_src_open (GstDecklinkAudioSrc * self)
|
||||||
|
{
|
||||||
|
GST_DEBUG_OBJECT (self, "Starting");
|
||||||
|
|
||||||
|
self->input =
|
||||||
|
gst_decklink_acquire_nth_input (self->device_number,
|
||||||
|
GST_ELEMENT_CAST (self), TRUE);
|
||||||
|
if (!self->input) {
|
||||||
|
GST_ERROR_OBJECT (self, "Failed to acquire input");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_mutex_lock (&self->input->lock);
|
||||||
|
self->input->got_audio_packet = gst_decklink_audio_src_got_packet;
|
||||||
|
g_mutex_unlock (&self->input->lock);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_decklink_audio_src_close (GstDecklinkAudioSrc * self)
|
||||||
|
{
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (self, "Stopping");
|
||||||
|
|
||||||
|
if (self->input) {
|
||||||
|
g_mutex_lock (&self->input->lock);
|
||||||
|
self->input->got_audio_packet = NULL;
|
||||||
|
g_mutex_unlock (&self->input->lock);
|
||||||
|
|
||||||
|
self->input->input->DisableAudioInput ();
|
||||||
|
gst_decklink_release_nth_input (self->device_number,
|
||||||
|
GST_ELEMENT_CAST (self), TRUE);
|
||||||
|
self->input = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
in_same_pipeline (GstElement * a, GstElement * b)
|
||||||
|
{
|
||||||
|
GstObject *root = NULL, *tmp;
|
||||||
|
gboolean ret = FALSE;
|
||||||
|
|
||||||
|
tmp = gst_object_get_parent (GST_OBJECT_CAST (a));
|
||||||
|
while (tmp != NULL) {
|
||||||
|
if (root)
|
||||||
|
gst_object_unref (root);
|
||||||
|
root = tmp;
|
||||||
|
tmp = gst_object_get_parent (root);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = root && gst_object_has_ancestor (GST_OBJECT_CAST (b), root);
|
||||||
|
|
||||||
|
if (root)
|
||||||
|
gst_object_unref (root);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstStateChangeReturn
|
||||||
|
gst_decklink_audio_src_change_state (GstElement * element,
|
||||||
|
GstStateChange transition)
|
||||||
|
{
|
||||||
|
GstDecklinkAudioSrc *self = GST_DECKLINK_AUDIO_SRC_CAST (element);
|
||||||
|
GstStateChangeReturn ret;
|
||||||
|
|
||||||
|
switch (transition) {
|
||||||
|
case GST_STATE_CHANGE_NULL_TO_READY:
|
||||||
|
if (!gst_decklink_audio_src_open (self)) {
|
||||||
|
ret = GST_STATE_CHANGE_FAILURE;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case GST_STATE_CHANGE_READY_TO_PAUSED:{
|
||||||
|
GstElement *videosrc = NULL;
|
||||||
|
|
||||||
|
// Check if there is a video src for this input too and if it
|
||||||
|
// is actually in the same pipeline
|
||||||
|
g_mutex_lock (&self->input->lock);
|
||||||
|
if (self->input->videosrc)
|
||||||
|
videosrc = GST_ELEMENT_CAST (gst_object_ref (self->input->videosrc));
|
||||||
|
g_mutex_unlock (&self->input->lock);
|
||||||
|
|
||||||
|
if (!videosrc) {
|
||||||
|
GST_ELEMENT_ERROR (self, STREAM, FAILED,
|
||||||
|
(NULL), ("Audio src needs a video src for its operation"));
|
||||||
|
ret = GST_STATE_CHANGE_FAILURE;
|
||||||
|
goto out;
|
||||||
|
} else if (!in_same_pipeline (GST_ELEMENT_CAST (self), videosrc)) {
|
||||||
|
GST_ELEMENT_ERROR (self, STREAM, FAILED,
|
||||||
|
(NULL),
|
||||||
|
("Audio src and video src need to be in the same pipeline"));
|
||||||
|
ret = GST_STATE_CHANGE_FAILURE;
|
||||||
|
gst_object_unref (videosrc);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (videosrc)
|
||||||
|
gst_object_unref (videosrc);
|
||||||
|
|
||||||
|
gst_element_post_message (element,
|
||||||
|
gst_message_new_clock_provide (GST_OBJECT_CAST (element),
|
||||||
|
self->input->clock, TRUE));
|
||||||
|
self->flushing = FALSE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:{
|
||||||
|
GstClock *clock;
|
||||||
|
|
||||||
|
clock = gst_element_get_clock (GST_ELEMENT_CAST (self));
|
||||||
|
if (clock && clock != self->input->clock) {
|
||||||
|
gst_clock_set_master (self->input->clock, clock);
|
||||||
|
}
|
||||||
|
if (clock)
|
||||||
|
gst_object_unref (clock);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
|
||||||
|
if (ret == GST_STATE_CHANGE_FAILURE)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
switch (transition) {
|
||||||
|
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
||||||
|
gst_element_post_message (element,
|
||||||
|
gst_message_new_clock_lost (GST_OBJECT_CAST (element),
|
||||||
|
self->input->clock));
|
||||||
|
gst_clock_set_master (self->input->clock, NULL);
|
||||||
|
|
||||||
|
if (self->current_packet) {
|
||||||
|
self->current_packet->Release ();
|
||||||
|
self->current_packet = NULL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
|
||||||
|
if (self->current_packet) {
|
||||||
|
self->current_packet->Release ();
|
||||||
|
self->current_packet = NULL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case GST_STATE_CHANGE_READY_TO_NULL:
|
||||||
|
gst_decklink_audio_src_close (self);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstClock *
|
||||||
|
gst_decklink_audio_src_provide_clock (GstElement * element)
|
||||||
|
{
|
||||||
|
GstDecklinkAudioSrc *self = GST_DECKLINK_AUDIO_SRC_CAST (element);
|
||||||
|
|
||||||
|
if (!self->input)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return GST_CLOCK_CAST (gst_object_ref (self->input->clock));
|
||||||
|
}
|
75
sys/decklink/gstdecklinkaudiosrc.h
Normal file
75
sys/decklink/gstdecklinkaudiosrc.h
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
/* GStreamer
|
||||||
|
*
|
||||||
|
* Copyright (C) 2011 David Schleef <ds@schleef.org>
|
||||||
|
* Copyright (C) 2014 Sebastian Dröge <sebastian@centricular.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __GST_DECKLINK_AUDIO_SRC_H__
|
||||||
|
#define __GST_DECKLINK_AUDIO_SRC_H__
|
||||||
|
|
||||||
|
#include <gst/gst.h>
|
||||||
|
#include <gst/base/base.h>
|
||||||
|
#include <gst/audio/audio.h>
|
||||||
|
#include "gstdecklink.h"
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
#define GST_TYPE_DECKLINK_AUDIO_SRC \
|
||||||
|
(gst_decklink_audio_src_get_type())
|
||||||
|
#define GST_DECKLINK_AUDIO_SRC(obj) \
|
||||||
|
(G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_DECKLINK_AUDIO_SRC, GstDecklinkAudioSrc))
|
||||||
|
#define GST_DECKLINK_AUDIO_SRC_CAST(obj) \
|
||||||
|
((GstDecklinkAudioSrc*)obj)
|
||||||
|
#define GST_DECKLINK_AUDIO_SRC_CLASS(klass) \
|
||||||
|
(G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_DECKLINK_AUDIO_SRC, GstDecklinkAudioSrcClass))
|
||||||
|
#define GST_IS_DECKLINK_AUDIO_SRC(obj) \
|
||||||
|
(G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_DECKLINK_AUDIO_SRC))
|
||||||
|
#define GST_IS_DECKLINK_AUDIO_SRC_CLASS(klass) \
|
||||||
|
(G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_DECKLINK_AUDIO_SRC))
|
||||||
|
|
||||||
|
typedef struct _GstDecklinkAudioSrc GstDecklinkAudioSrc;
|
||||||
|
typedef struct _GstDecklinkAudioSrcClass GstDecklinkAudioSrcClass;
|
||||||
|
|
||||||
|
struct _GstDecklinkAudioSrc
|
||||||
|
{
|
||||||
|
GstPushSrc parent;
|
||||||
|
|
||||||
|
GstDecklinkModeEnum mode;
|
||||||
|
gint device_number;
|
||||||
|
|
||||||
|
GstAudioInfo info;
|
||||||
|
|
||||||
|
GstDecklinkInput *input;
|
||||||
|
|
||||||
|
GCond cond;
|
||||||
|
GMutex lock;
|
||||||
|
gboolean flushing;
|
||||||
|
IDeckLinkAudioInputPacket *current_packet;
|
||||||
|
GstClockTime current_packet_capture_time;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _GstDecklinkAudioSrcClass
|
||||||
|
{
|
||||||
|
GstPushSrcClass parent_class;
|
||||||
|
};
|
||||||
|
|
||||||
|
GType gst_decklink_audio_src_get_type (void);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __GST_DECKLINK_AUDIO_SRC_H__ */
|
461
sys/decklink/gstdecklinkvideosrc.cpp
Normal file
461
sys/decklink/gstdecklinkvideosrc.cpp
Normal file
|
@ -0,0 +1,461 @@
|
||||||
|
/* GStreamer
|
||||||
|
* Copyright (C) 2011 David Schleef <ds@entropywave.com>
|
||||||
|
* Copyright (C) 2014 Sebastian Dröge <sebastian@centricular.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 51 Franklin Street, Suite 500,
|
||||||
|
* Boston, MA 02110-1335, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "gstdecklinkvideosrc.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
GST_DEBUG_CATEGORY_STATIC (gst_decklink_video_src_debug);
|
||||||
|
#define GST_CAT_DEFAULT gst_decklink_video_src_debug
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
PROP_0,
|
||||||
|
PROP_MODE,
|
||||||
|
PROP_DEVICE_NUMBER
|
||||||
|
};
|
||||||
|
|
||||||
|
static void gst_decklink_video_src_set_property (GObject * object,
|
||||||
|
guint property_id, const GValue * value, GParamSpec * pspec);
|
||||||
|
static void gst_decklink_video_src_get_property (GObject * object,
|
||||||
|
guint property_id, GValue * value, GParamSpec * pspec);
|
||||||
|
static void gst_decklink_video_src_finalize (GObject * object);
|
||||||
|
|
||||||
|
static GstStateChangeReturn
|
||||||
|
gst_decklink_video_src_change_state (GstElement * element,
|
||||||
|
GstStateChange transition);
|
||||||
|
static GstClock *gst_decklink_video_src_provide_clock (GstElement * element);
|
||||||
|
|
||||||
|
static GstCaps *gst_decklink_video_src_get_caps (GstBaseSrc * bsrc,
|
||||||
|
GstCaps * filter);
|
||||||
|
static gboolean gst_decklink_video_src_unlock (GstBaseSrc * bsrc);
|
||||||
|
static gboolean gst_decklink_video_src_unlock_stop (GstBaseSrc * bsrc);
|
||||||
|
|
||||||
|
static GstFlowReturn gst_decklink_video_src_create (GstPushSrc * psrc,
|
||||||
|
GstBuffer ** buffer);
|
||||||
|
|
||||||
|
static gboolean gst_decklink_video_src_open (GstDecklinkVideoSrc * self);
|
||||||
|
static gboolean gst_decklink_video_src_close (GstDecklinkVideoSrc * self);
|
||||||
|
|
||||||
|
#define parent_class gst_decklink_video_src_parent_class
|
||||||
|
G_DEFINE_TYPE (GstDecklinkVideoSrc, gst_decklink_video_src, GST_TYPE_PUSH_SRC);
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_decklink_video_src_class_init (GstDecklinkVideoSrcClass * klass)
|
||||||
|
{
|
||||||
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||||
|
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
|
||||||
|
GstBaseSrcClass *basesrc_class = GST_BASE_SRC_CLASS (klass);
|
||||||
|
GstPushSrcClass *pushsrc_class = GST_PUSH_SRC_CLASS (klass);
|
||||||
|
GstCaps *templ_caps;
|
||||||
|
|
||||||
|
gobject_class->set_property = gst_decklink_video_src_set_property;
|
||||||
|
gobject_class->get_property = gst_decklink_video_src_get_property;
|
||||||
|
gobject_class->finalize = gst_decklink_video_src_finalize;
|
||||||
|
|
||||||
|
element_class->change_state =
|
||||||
|
GST_DEBUG_FUNCPTR (gst_decklink_video_src_change_state);
|
||||||
|
element_class->provide_clock =
|
||||||
|
GST_DEBUG_FUNCPTR (gst_decklink_video_src_provide_clock);
|
||||||
|
|
||||||
|
basesrc_class->get_caps = GST_DEBUG_FUNCPTR (gst_decklink_video_src_get_caps);
|
||||||
|
basesrc_class->unlock = GST_DEBUG_FUNCPTR (gst_decklink_video_src_unlock);
|
||||||
|
basesrc_class->unlock_stop =
|
||||||
|
GST_DEBUG_FUNCPTR (gst_decklink_video_src_unlock_stop);
|
||||||
|
|
||||||
|
pushsrc_class->create = GST_DEBUG_FUNCPTR (gst_decklink_video_src_create);
|
||||||
|
|
||||||
|
g_object_class_install_property (gobject_class, PROP_MODE,
|
||||||
|
g_param_spec_enum ("mode", "Playback Mode",
|
||||||
|
"Video Mode to use for playback",
|
||||||
|
GST_TYPE_DECKLINK_MODE, GST_DECKLINK_MODE_NTSC,
|
||||||
|
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
|
||||||
|
G_PARAM_CONSTRUCT)));
|
||||||
|
|
||||||
|
g_object_class_install_property (gobject_class, PROP_DEVICE_NUMBER,
|
||||||
|
g_param_spec_int ("device-number", "Device number",
|
||||||
|
"Output device instance to use", 0, G_MAXINT, 0,
|
||||||
|
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
|
||||||
|
G_PARAM_CONSTRUCT)));
|
||||||
|
|
||||||
|
templ_caps = gst_decklink_mode_get_template_caps ();
|
||||||
|
gst_element_class_add_pad_template (element_class,
|
||||||
|
gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, templ_caps));
|
||||||
|
gst_caps_unref (templ_caps);
|
||||||
|
|
||||||
|
gst_element_class_set_static_metadata (element_class, "Decklink Video Source",
|
||||||
|
"Video/Src", "Decklink Source", "David Schleef <ds@entropywave.com>, "
|
||||||
|
"Sebastian Dröge <sebastian@centricular.com>");
|
||||||
|
|
||||||
|
GST_DEBUG_CATEGORY_INIT (gst_decklink_video_src_debug, "decklinkvideosrc",
|
||||||
|
0, "debug category for decklinkvideosrc element");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_decklink_video_src_init (GstDecklinkVideoSrc * self)
|
||||||
|
{
|
||||||
|
self->mode = GST_DECKLINK_MODE_NTSC;
|
||||||
|
self->device_number = 0;
|
||||||
|
|
||||||
|
gst_base_src_set_live (GST_BASE_SRC (self), TRUE);
|
||||||
|
gst_base_src_set_format (GST_BASE_SRC (self), GST_FORMAT_TIME);
|
||||||
|
|
||||||
|
g_mutex_init (&self->lock);
|
||||||
|
g_cond_init (&self->cond);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
gst_decklink_video_src_set_property (GObject * object, guint property_id,
|
||||||
|
const GValue * value, GParamSpec * pspec)
|
||||||
|
{
|
||||||
|
GstDecklinkVideoSrc *self = GST_DECKLINK_VIDEO_SRC_CAST (object);
|
||||||
|
|
||||||
|
switch (property_id) {
|
||||||
|
case PROP_MODE:
|
||||||
|
self->mode = (GstDecklinkModeEnum) g_value_get_enum (value);
|
||||||
|
break;
|
||||||
|
case PROP_DEVICE_NUMBER:
|
||||||
|
self->device_number = g_value_get_int (value);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
gst_decklink_video_src_get_property (GObject * object, guint property_id,
|
||||||
|
GValue * value, GParamSpec * pspec)
|
||||||
|
{
|
||||||
|
GstDecklinkVideoSrc *self = GST_DECKLINK_VIDEO_SRC_CAST (object);
|
||||||
|
|
||||||
|
switch (property_id) {
|
||||||
|
case PROP_MODE:
|
||||||
|
g_value_set_enum (value, self->mode);
|
||||||
|
break;
|
||||||
|
case PROP_DEVICE_NUMBER:
|
||||||
|
g_value_set_int (value, self->device_number);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
gst_decklink_video_src_finalize (GObject * object)
|
||||||
|
{
|
||||||
|
GstDecklinkVideoSrc *self = GST_DECKLINK_VIDEO_SRC_CAST (object);
|
||||||
|
|
||||||
|
g_mutex_clear (&self->lock);
|
||||||
|
g_cond_clear (&self->cond);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstCaps *
|
||||||
|
gst_decklink_video_src_get_caps (GstBaseSrc * bsrc, GstCaps * filter)
|
||||||
|
{
|
||||||
|
GstDecklinkVideoSrc *self = GST_DECKLINK_VIDEO_SRC_CAST (bsrc);
|
||||||
|
GstCaps *mode_caps, *caps;
|
||||||
|
|
||||||
|
mode_caps = gst_decklink_mode_get_caps (self->mode);
|
||||||
|
if (filter) {
|
||||||
|
caps =
|
||||||
|
gst_caps_intersect_full (filter, mode_caps, GST_CAPS_INTERSECT_FIRST);
|
||||||
|
gst_caps_unref (mode_caps);
|
||||||
|
} else {
|
||||||
|
caps = mode_caps;
|
||||||
|
}
|
||||||
|
|
||||||
|
return caps;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_decklink_video_src_got_frame (GstElement * element,
|
||||||
|
IDeckLinkVideoInputFrame * frame, GstClockTime capture_time)
|
||||||
|
{
|
||||||
|
GstDecklinkVideoSrc *self = GST_DECKLINK_VIDEO_SRC_CAST (element);
|
||||||
|
|
||||||
|
GST_LOG_OBJECT (self, "Got video frame at %" GST_TIME_FORMAT, GST_TIME_ARGS (capture_time));
|
||||||
|
|
||||||
|
g_mutex_lock (&self->lock);
|
||||||
|
if (!self->flushing) {
|
||||||
|
if (self->current_frame)
|
||||||
|
self->current_frame->Release ();
|
||||||
|
self->current_frame = frame;
|
||||||
|
frame->AddRef ();
|
||||||
|
self->current_frame_capture_time = capture_time;
|
||||||
|
g_cond_signal (&self->cond);
|
||||||
|
}
|
||||||
|
g_mutex_unlock (&self->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
IDeckLinkVideoInputFrame *frame;
|
||||||
|
IDeckLinkInput *input;
|
||||||
|
} VideoFrame;
|
||||||
|
|
||||||
|
static void
|
||||||
|
video_frame_free (void *data)
|
||||||
|
{
|
||||||
|
VideoFrame *video_frame = (VideoFrame *) data;
|
||||||
|
|
||||||
|
video_frame->frame->Release ();
|
||||||
|
video_frame->input->Release ();
|
||||||
|
g_free (video_frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstFlowReturn
|
||||||
|
gst_decklink_video_src_create (GstPushSrc * bsrc, GstBuffer ** buffer)
|
||||||
|
{
|
||||||
|
GstDecklinkVideoSrc *self = GST_DECKLINK_VIDEO_SRC_CAST (bsrc);
|
||||||
|
GstFlowReturn flow_ret = GST_FLOW_OK;
|
||||||
|
IDeckLinkVideoInputFrame *frame = NULL;
|
||||||
|
GstClockTime capture_time = GST_CLOCK_TIME_NONE;
|
||||||
|
const guint8 *data;
|
||||||
|
gsize data_size;
|
||||||
|
VideoFrame *vf;
|
||||||
|
|
||||||
|
g_mutex_lock (&self->lock);
|
||||||
|
while (!self->current_frame && !self->flushing) {
|
||||||
|
g_cond_wait (&self->cond, &self->lock);
|
||||||
|
}
|
||||||
|
frame = self->current_frame;
|
||||||
|
capture_time = self->current_frame_capture_time;
|
||||||
|
self->current_frame = NULL;
|
||||||
|
g_mutex_unlock (&self->lock);
|
||||||
|
|
||||||
|
if (self->flushing) {
|
||||||
|
if (frame)
|
||||||
|
frame->Release ();
|
||||||
|
return GST_FLOW_FLUSHING;
|
||||||
|
}
|
||||||
|
|
||||||
|
frame->GetBytes ((gpointer *) & data);
|
||||||
|
|
||||||
|
data_size = self->info.size;
|
||||||
|
|
||||||
|
|
||||||
|
vf = (VideoFrame *) g_malloc0 (sizeof (VideoFrame));
|
||||||
|
|
||||||
|
*buffer =
|
||||||
|
gst_buffer_new_wrapped_full ((GstMemoryFlags) GST_MEMORY_FLAG_READONLY,
|
||||||
|
(gpointer) data, data_size, 0, data_size, vf,
|
||||||
|
(GDestroyNotify) video_frame_free);
|
||||||
|
|
||||||
|
vf->frame = frame;
|
||||||
|
vf->input = self->input->input;
|
||||||
|
vf->input->AddRef ();
|
||||||
|
|
||||||
|
GST_BUFFER_TIMESTAMP (*buffer) = capture_time;
|
||||||
|
GST_BUFFER_DURATION (*buffer) = gst_util_uint64_scale_int (GST_SECOND,
|
||||||
|
self->info.fps_d, self->info.fps_n);
|
||||||
|
|
||||||
|
return flow_ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_decklink_video_src_unlock (GstBaseSrc * bsrc)
|
||||||
|
{
|
||||||
|
GstDecklinkVideoSrc *self = GST_DECKLINK_VIDEO_SRC_CAST (bsrc);
|
||||||
|
|
||||||
|
g_mutex_lock (&self->lock);
|
||||||
|
self->flushing = TRUE;
|
||||||
|
g_cond_signal (&self->cond);
|
||||||
|
g_mutex_unlock (&self->lock);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_decklink_video_src_unlock_stop (GstBaseSrc * bsrc)
|
||||||
|
{
|
||||||
|
GstDecklinkVideoSrc *self = GST_DECKLINK_VIDEO_SRC_CAST (bsrc);
|
||||||
|
|
||||||
|
g_mutex_lock (&self->lock);
|
||||||
|
self->flushing = FALSE;
|
||||||
|
g_mutex_unlock (&self->lock);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_decklink_video_src_open (GstDecklinkVideoSrc * self)
|
||||||
|
{
|
||||||
|
const GstDecklinkMode *mode;
|
||||||
|
GstCaps *caps;
|
||||||
|
HRESULT ret;
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (self, "Starting");
|
||||||
|
|
||||||
|
self->input =
|
||||||
|
gst_decklink_acquire_nth_input (self->device_number,
|
||||||
|
GST_ELEMENT_CAST (self), FALSE);
|
||||||
|
if (!self->input) {
|
||||||
|
GST_ERROR_OBJECT (self, "Failed to acquire input");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
mode = gst_decklink_get_mode (self->mode);
|
||||||
|
g_assert (mode != NULL);
|
||||||
|
|
||||||
|
ret = self->input->input->EnableVideoInput (mode->mode,
|
||||||
|
bmdFormat8BitYUV, bmdVideoOutputFlagDefault);
|
||||||
|
if (ret != S_OK) {
|
||||||
|
GST_WARNING_OBJECT (self, "Failed to enable video input");
|
||||||
|
gst_decklink_release_nth_input (self->device_number,
|
||||||
|
GST_ELEMENT_CAST (self), FALSE);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_mutex_lock (&self->input->lock);
|
||||||
|
self->input->got_video_frame = gst_decklink_video_src_got_frame;
|
||||||
|
g_mutex_unlock (&self->input->lock);
|
||||||
|
|
||||||
|
caps = gst_decklink_mode_get_caps (self->mode);
|
||||||
|
gst_video_info_from_caps (&self->info, caps);
|
||||||
|
gst_caps_unref (caps);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_decklink_video_src_close (GstDecklinkVideoSrc * self)
|
||||||
|
{
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (self, "Stopping");
|
||||||
|
|
||||||
|
if (self->input) {
|
||||||
|
g_mutex_lock (&self->input->lock);
|
||||||
|
self->input->got_video_frame = NULL;
|
||||||
|
g_mutex_unlock (&self->input->lock);
|
||||||
|
|
||||||
|
self->input->input->DisableVideoInput ();
|
||||||
|
gst_decklink_release_nth_input (self->device_number,
|
||||||
|
GST_ELEMENT_CAST (self), FALSE);
|
||||||
|
self->input = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstStateChangeReturn
|
||||||
|
gst_decklink_video_src_change_state (GstElement * element,
|
||||||
|
GstStateChange transition)
|
||||||
|
{
|
||||||
|
GstDecklinkVideoSrc *self = GST_DECKLINK_VIDEO_SRC_CAST (element);
|
||||||
|
GstStateChangeReturn ret;
|
||||||
|
|
||||||
|
|
||||||
|
switch (transition) {
|
||||||
|
case GST_STATE_CHANGE_NULL_TO_READY:
|
||||||
|
if (!gst_decklink_video_src_open (self)) {
|
||||||
|
ret = GST_STATE_CHANGE_FAILURE;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case GST_STATE_CHANGE_READY_TO_PAUSED:
|
||||||
|
gst_element_post_message (element,
|
||||||
|
gst_message_new_clock_provide (GST_OBJECT_CAST (element),
|
||||||
|
self->input->clock, TRUE));
|
||||||
|
self->flushing = FALSE;
|
||||||
|
break;
|
||||||
|
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:{
|
||||||
|
GstClock *clock;
|
||||||
|
|
||||||
|
clock = gst_element_get_clock (GST_ELEMENT_CAST (self));
|
||||||
|
if (clock && clock != self->input->clock) {
|
||||||
|
gst_clock_set_master (self->input->clock, clock);
|
||||||
|
}
|
||||||
|
if (clock)
|
||||||
|
gst_object_unref (clock);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
|
||||||
|
if (ret == GST_STATE_CHANGE_FAILURE)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
switch (transition) {
|
||||||
|
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
||||||
|
gst_element_post_message (element,
|
||||||
|
gst_message_new_clock_lost (GST_OBJECT_CAST (element),
|
||||||
|
self->input->clock));
|
||||||
|
gst_clock_set_master (self->input->clock, NULL);
|
||||||
|
break;
|
||||||
|
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:{
|
||||||
|
HRESULT res;
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (self, "Stopping streams");
|
||||||
|
res = self->input->input->StopStreams ();
|
||||||
|
if (res != S_OK) {
|
||||||
|
GST_ELEMENT_ERROR (self, STREAM, FAILED,
|
||||||
|
(NULL), ("Failed to stop streams: 0x%08x", res));
|
||||||
|
ret = GST_STATE_CHANGE_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->current_frame) {
|
||||||
|
self->current_frame->Release ();
|
||||||
|
self->current_frame = NULL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:{
|
||||||
|
HRESULT res;
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (self, "Starting streams");
|
||||||
|
res = self->input->input->StartStreams ();
|
||||||
|
if (res != S_OK) {
|
||||||
|
GST_ELEMENT_ERROR (self, STREAM, FAILED,
|
||||||
|
(NULL), ("Failed to start streams: 0x%08x", res));
|
||||||
|
ret = GST_STATE_CHANGE_FAILURE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GST_STATE_CHANGE_READY_TO_NULL:
|
||||||
|
gst_decklink_video_src_close (self);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstClock *
|
||||||
|
gst_decklink_video_src_provide_clock (GstElement * element)
|
||||||
|
{
|
||||||
|
GstDecklinkVideoSrc *self = GST_DECKLINK_VIDEO_SRC_CAST (element);
|
||||||
|
|
||||||
|
if (!self->input)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return GST_CLOCK_CAST (gst_object_ref (self->input->clock));
|
||||||
|
}
|
75
sys/decklink/gstdecklinkvideosrc.h
Normal file
75
sys/decklink/gstdecklinkvideosrc.h
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
/* GStreamer
|
||||||
|
*
|
||||||
|
* Copyright (C) 2011 David Schleef <ds@schleef.org>
|
||||||
|
* Copyright (C) 2014 Sebastian Dröge <sebastian@centricular.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __GST_DECKLINK_VIDEO_SRC_H__
|
||||||
|
#define __GST_DECKLINK_VIDEO_SRC_H__
|
||||||
|
|
||||||
|
#include <gst/gst.h>
|
||||||
|
#include <gst/base/base.h>
|
||||||
|
#include <gst/video/video.h>
|
||||||
|
#include "gstdecklink.h"
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
#define GST_TYPE_DECKLINK_VIDEO_SRC \
|
||||||
|
(gst_decklink_video_src_get_type())
|
||||||
|
#define GST_DECKLINK_VIDEO_SRC(obj) \
|
||||||
|
(G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_DECKLINK_VIDEO_SRC, GstDecklinkVideoSrc))
|
||||||
|
#define GST_DECKLINK_VIDEO_SRC_CAST(obj) \
|
||||||
|
((GstDecklinkVideoSrc*)obj)
|
||||||
|
#define GST_DECKLINK_VIDEO_SRC_CLASS(klass) \
|
||||||
|
(G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_DECKLINK_VIDEO_SRC, GstDecklinkVideoSrcClass))
|
||||||
|
#define GST_IS_DECKLINK_VIDEO_SRC(obj) \
|
||||||
|
(G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_DECKLINK_VIDEO_SRC))
|
||||||
|
#define GST_IS_DECKLINK_VIDEO_SRC_CLASS(klass) \
|
||||||
|
(G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_DECKLINK_VIDEO_SRC))
|
||||||
|
|
||||||
|
typedef struct _GstDecklinkVideoSrc GstDecklinkVideoSrc;
|
||||||
|
typedef struct _GstDecklinkVideoSrcClass GstDecklinkVideoSrcClass;
|
||||||
|
|
||||||
|
struct _GstDecklinkVideoSrc
|
||||||
|
{
|
||||||
|
GstPushSrc parent;
|
||||||
|
|
||||||
|
GstDecklinkModeEnum mode;
|
||||||
|
gint device_number;
|
||||||
|
|
||||||
|
GstVideoInfo info;
|
||||||
|
|
||||||
|
GstDecklinkInput *input;
|
||||||
|
|
||||||
|
GCond cond;
|
||||||
|
GMutex lock;
|
||||||
|
gboolean flushing;
|
||||||
|
IDeckLinkVideoInputFrame *current_frame;
|
||||||
|
GstClockTime current_frame_capture_time;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _GstDecklinkVideoSrcClass
|
||||||
|
{
|
||||||
|
GstPushSrcClass parent_class;
|
||||||
|
};
|
||||||
|
|
||||||
|
GType gst_decklink_video_src_get_type (void);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __GST_DECKLINK_VIDEO_SRC_H__ */
|
Loading…
Reference in a new issue