623 lines
18 KiB
C
623 lines
18 KiB
C
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#include "emotion_gstreamer.h"
|
|
|
|
static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE("sink",
|
|
GST_PAD_SINK, GST_PAD_ALWAYS,
|
|
GST_STATIC_CAPS(GST_VIDEO_CAPS_MAKE("{ I420, YV12, YUY2, NV12, BGRx, BGR, BGRA }")));
|
|
|
|
GST_DEBUG_CATEGORY_STATIC(emotion_video_sink_debug);
|
|
#define GST_CAT_DEFAULT emotion_video_sink_debug
|
|
|
|
enum {
|
|
LAST_SIGNAL
|
|
};
|
|
|
|
enum {
|
|
PROP_0,
|
|
PROP_EMOTION_OBJECT,
|
|
PROP_LAST
|
|
};
|
|
|
|
#define _do_init \
|
|
GST_DEBUG_CATEGORY_INIT(emotion_video_sink_debug, \
|
|
"emotion-sink", \
|
|
0, \
|
|
"emotion video sink")
|
|
|
|
#define parent_class emotion_video_sink_parent_class
|
|
G_DEFINE_TYPE_WITH_CODE(EmotionVideoSink,
|
|
emotion_video_sink,
|
|
GST_TYPE_VIDEO_SINK,
|
|
G_ADD_PRIVATE(EmotionVideoSink)
|
|
_do_init);
|
|
|
|
|
|
static void unlock_buffer_mutex(EmotionVideoSinkPrivate* priv);
|
|
static void emotion_video_sink_main_render(void *data);
|
|
|
|
static void
|
|
emotion_video_sink_init(EmotionVideoSink* sink)
|
|
{
|
|
EmotionVideoSinkPrivate* priv;
|
|
|
|
INF("sink init");
|
|
sink->priv = priv = emotion_video_sink_get_instance_private(sink);
|
|
gst_video_info_init (&priv->info);
|
|
priv->eheight = 0;
|
|
priv->func = NULL;
|
|
priv->eformat = EVAS_COLORSPACE_ARGB8888;
|
|
eina_lock_new(&priv->m);
|
|
eina_condition_new(&priv->c, &priv->m);
|
|
priv->unlocked = EINA_FALSE;
|
|
}
|
|
|
|
/**** Object methods ****/
|
|
static void
|
|
_cleanup_priv(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED)
|
|
{
|
|
EmotionVideoSinkPrivate* priv;
|
|
|
|
priv = data;
|
|
|
|
eina_lock_take(&priv->m);
|
|
if (priv->evas_object == obj)
|
|
priv->evas_object = NULL;
|
|
eina_lock_release(&priv->m);
|
|
}
|
|
|
|
static void
|
|
emotion_video_sink_set_property(GObject * object, guint prop_id,
|
|
const GValue * value, GParamSpec * pspec)
|
|
{
|
|
EmotionVideoSink* sink;
|
|
EmotionVideoSinkPrivate* priv;
|
|
|
|
sink = EMOTION_VIDEO_SINK (object);
|
|
priv = sink->priv;
|
|
|
|
switch (prop_id) {
|
|
case PROP_EMOTION_OBJECT:
|
|
eina_lock_take(&priv->m);
|
|
if (priv->evas_object)
|
|
evas_object_event_callback_del(priv->evas_object, EVAS_CALLBACK_DEL, _cleanup_priv);
|
|
priv->emotion_object = g_value_get_pointer (value);
|
|
INF("sink set Emotion object %p", priv->emotion_object);
|
|
if (priv->emotion_object)
|
|
{
|
|
priv->evas_object = emotion_object_image_get(priv->emotion_object);
|
|
if (priv->evas_object)
|
|
{
|
|
evas_object_event_callback_add(priv->evas_object, EVAS_CALLBACK_DEL, _cleanup_priv, priv);
|
|
evas_object_image_pixels_get_callback_set(priv->evas_object, NULL, NULL);
|
|
}
|
|
}
|
|
eina_lock_release(&priv->m);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
ERR("invalid property");
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
emotion_video_sink_get_property(GObject * object, guint prop_id,
|
|
GValue * value, GParamSpec * pspec)
|
|
{
|
|
EmotionVideoSink* sink;
|
|
EmotionVideoSinkPrivate* priv;
|
|
|
|
sink = EMOTION_VIDEO_SINK (object);
|
|
priv = sink->priv;
|
|
|
|
switch (prop_id) {
|
|
case PROP_EMOTION_OBJECT:
|
|
INF("sink get property.");
|
|
eina_lock_take(&priv->m);
|
|
g_value_set_pointer(value, priv->emotion_object);
|
|
eina_lock_release(&priv->m);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
ERR("invalide property");
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
emotion_video_sink_dispose(GObject* object)
|
|
{
|
|
EmotionVideoSink* sink;
|
|
EmotionVideoSinkPrivate* priv;
|
|
|
|
INF("dispose.");
|
|
|
|
sink = EMOTION_VIDEO_SINK(object);
|
|
priv = sink->priv;
|
|
|
|
eina_lock_take(&priv->m);
|
|
if (priv->vfmapped)
|
|
{
|
|
if (priv->evas_object)
|
|
{
|
|
evas_object_image_size_set(priv->evas_object, 1, 1);
|
|
evas_object_image_data_set(priv->evas_object, NULL);
|
|
}
|
|
gst_video_frame_unmap(&(priv->last_vframe));
|
|
priv->vfmapped = EINA_FALSE;
|
|
}
|
|
else
|
|
{
|
|
if ((priv->mapped) && (priv->last_buffer))
|
|
{
|
|
if (priv->evas_object)
|
|
{
|
|
evas_object_image_size_set(priv->evas_object, 1, 1);
|
|
evas_object_image_data_set(priv->evas_object, NULL);
|
|
}
|
|
gst_buffer_unmap(priv->last_buffer, &(priv->map_info));
|
|
priv->mapped = EINA_FALSE;
|
|
}
|
|
}
|
|
if (priv->last_buffer)
|
|
{
|
|
gst_buffer_unref(priv->last_buffer);
|
|
priv->last_buffer = NULL;
|
|
}
|
|
|
|
eina_lock_release(&priv->m);
|
|
eina_lock_free(&priv->m);
|
|
eina_condition_free(&priv->c);
|
|
|
|
G_OBJECT_CLASS(parent_class)->dispose(object);
|
|
}
|
|
|
|
|
|
/**** BaseSink methods ****/
|
|
|
|
gboolean emotion_video_sink_set_caps(GstBaseSink *bsink, GstCaps *caps)
|
|
{
|
|
EmotionVideoSink* sink;
|
|
EmotionVideoSinkPrivate* priv;
|
|
GstVideoInfo info;
|
|
unsigned int i;
|
|
|
|
sink = EMOTION_VIDEO_SINK(bsink);
|
|
priv = sink->priv;
|
|
|
|
if (!gst_video_info_from_caps(&info, caps))
|
|
{
|
|
ERR("Unable to parse caps.");
|
|
return FALSE;
|
|
}
|
|
|
|
priv->info = info;
|
|
priv->eheight = info.height;
|
|
|
|
for (i = 0; colorspace_format_convertion[i].name; i++)
|
|
{
|
|
if ((info.finfo->format == colorspace_format_convertion[i].format) &&
|
|
((colorspace_format_convertion[i].colormatrix == GST_VIDEO_COLOR_MATRIX_UNKNOWN) ||
|
|
(colorspace_format_convertion[i].colormatrix == info.colorimetry.matrix)))
|
|
{
|
|
DBG("Found '%s'", colorspace_format_convertion[i].name);
|
|
priv->eformat = colorspace_format_convertion[i].eformat;
|
|
priv->func = colorspace_format_convertion[i].func;
|
|
if (colorspace_format_convertion[i].force_height)
|
|
{
|
|
priv->eheight = (priv->eheight >> 1) << 1;
|
|
}
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
ERR("unsupported : %s\n", gst_video_format_to_string(info.finfo->format));
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
emotion_video_sink_start(GstBaseSink* base_sink)
|
|
{
|
|
EmotionVideoSinkPrivate* priv;
|
|
gboolean res = TRUE;
|
|
|
|
INF("sink start");
|
|
|
|
priv = EMOTION_VIDEO_SINK(base_sink)->priv;
|
|
eina_lock_take(&priv->m);
|
|
if (!priv->emotion_object)
|
|
res = FALSE;
|
|
else
|
|
priv->unlocked = EINA_FALSE;
|
|
eina_lock_release(&priv->m);
|
|
|
|
priv->frames = priv->rlapse = priv->flapse = 0;
|
|
|
|
return res;
|
|
}
|
|
|
|
static gboolean
|
|
emotion_video_sink_stop(GstBaseSink* base_sink)
|
|
{
|
|
EmotionVideoSinkPrivate* priv = EMOTION_VIDEO_SINK(base_sink)->priv;
|
|
|
|
INF("sink stop");
|
|
|
|
eina_lock_take(&priv->m);
|
|
if (priv->vfmapped)
|
|
{
|
|
if (priv->evas_object)
|
|
{
|
|
evas_object_image_size_set(priv->evas_object, 1, 1);
|
|
evas_object_image_data_set(priv->evas_object, NULL);
|
|
}
|
|
gst_video_frame_unmap(&(priv->last_vframe));
|
|
priv->vfmapped = EINA_FALSE;
|
|
}
|
|
if (priv->last_buffer)
|
|
{
|
|
if (priv->evas_object)
|
|
{
|
|
evas_object_image_size_set(priv->evas_object, 1, 1);
|
|
evas_object_image_data_set(priv->evas_object, NULL);
|
|
}
|
|
if (priv->mapped)
|
|
gst_buffer_unmap(priv->last_buffer, &(priv->map_info));
|
|
priv->mapped = EINA_FALSE;
|
|
gst_buffer_unref(priv->last_buffer);
|
|
priv->last_buffer = NULL;
|
|
}
|
|
|
|
/* If there still is a pending frame, neutralize it */
|
|
if (priv->send)
|
|
{
|
|
gst_buffer_replace(&priv->send->frame, NULL);
|
|
priv->send = NULL;
|
|
}
|
|
|
|
unlock_buffer_mutex(priv);
|
|
eina_lock_release(&priv->m);
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
emotion_video_sink_unlock(GstBaseSink* object)
|
|
{
|
|
EmotionVideoSink* sink;
|
|
|
|
INF("sink unlock");
|
|
|
|
sink = EMOTION_VIDEO_SINK(object);
|
|
|
|
eina_lock_take(&sink->priv->m);
|
|
unlock_buffer_mutex(sink->priv);
|
|
eina_lock_release(&sink->priv->m);
|
|
|
|
return GST_CALL_PARENT_WITH_DEFAULT(GST_BASE_SINK_CLASS, unlock,
|
|
(object), TRUE);
|
|
}
|
|
|
|
static gboolean
|
|
emotion_video_sink_unlock_stop(GstBaseSink* object)
|
|
{
|
|
EmotionVideoSink* sink;
|
|
EmotionVideoSinkPrivate* priv;
|
|
|
|
sink = EMOTION_VIDEO_SINK(object);
|
|
priv = sink->priv;
|
|
|
|
INF("sink unlock stop");
|
|
|
|
eina_lock_take(&priv->m);
|
|
priv->unlocked = FALSE;
|
|
eina_lock_release(&priv->m);
|
|
|
|
return GST_CALL_PARENT_WITH_DEFAULT(GST_BASE_SINK_CLASS, unlock_stop,
|
|
(object), TRUE);
|
|
}
|
|
|
|
static GstFlowReturn
|
|
emotion_video_sink_show_frame(GstVideoSink* vsink, GstBuffer* buffer)
|
|
{
|
|
Emotion_Gstreamer_Buffer *send;
|
|
EmotionVideoSinkPrivate *priv;
|
|
EmotionVideoSink *sink;
|
|
|
|
INF("sink render %p", buffer);
|
|
|
|
sink = EMOTION_VIDEO_SINK(vsink);
|
|
priv = sink->priv;
|
|
|
|
eina_lock_take(&priv->m);
|
|
if (priv->unlocked) {
|
|
ERR("LOCKED");
|
|
eina_lock_release(&priv->m);
|
|
return GST_FLOW_FLUSHING;
|
|
}
|
|
|
|
send = emotion_gstreamer_buffer_alloc(sink, buffer, &priv->info, priv->eformat, priv->eheight, priv->func);
|
|
|
|
/* If there still is a pending frame, neutralize it */
|
|
if (priv->send)
|
|
{
|
|
gst_buffer_replace(&priv->send->frame, NULL);
|
|
}
|
|
priv->send = send;
|
|
|
|
if (!send) {
|
|
eina_lock_release(&priv->m);
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
|
|
_emotion_pending_ecore_begin();
|
|
ecore_main_loop_thread_safe_call_async(emotion_video_sink_main_render, send);
|
|
|
|
eina_condition_wait(&priv->c);
|
|
eina_lock_release(&priv->m);
|
|
|
|
return GST_FLOW_OK;
|
|
}
|
|
|
|
static void
|
|
_update_emotion_fps(EmotionVideoSinkPrivate *priv)
|
|
{
|
|
double tim;
|
|
|
|
if (!debug_fps) return;
|
|
|
|
tim = ecore_time_get();
|
|
priv->frames++;
|
|
|
|
if (EINA_DBL_EQ(priv->rlapse, 0.0))
|
|
{
|
|
priv->rlapse = tim;
|
|
priv->flapse = priv->frames;
|
|
}
|
|
else if ((tim - priv->rlapse) >= 0.5)
|
|
{
|
|
priv->rlapse = tim;
|
|
priv->flapse = priv->frames;
|
|
}
|
|
}
|
|
|
|
static void
|
|
emotion_video_sink_main_render(void *data)
|
|
{
|
|
Emotion_Gstreamer_Buffer *send;
|
|
EmotionVideoSinkPrivate *priv;
|
|
GstBuffer *buffer = NULL;
|
|
GstMapInfo map;
|
|
unsigned char *evas_data;
|
|
double ratio;
|
|
Emotion_Convert_Info info;
|
|
|
|
send = data;
|
|
|
|
priv = send->sink->priv;
|
|
|
|
eina_lock_take(&priv->m);
|
|
/* Sink was shut down already or this is a stale
|
|
* frame */
|
|
if (priv->send != send)
|
|
goto exit_point;
|
|
if (!send->frame)
|
|
goto exit_point;
|
|
|
|
priv->send = NULL;
|
|
|
|
if (!priv->emotion_object || priv->unlocked)
|
|
goto exit_point;
|
|
|
|
/* Can happen if cleanup_priv was called */
|
|
if (!priv->evas_object)
|
|
{
|
|
priv->evas_object = emotion_object_image_get(priv->emotion_object);
|
|
if (priv->evas_object)
|
|
{
|
|
evas_object_event_callback_add(priv->evas_object, EVAS_CALLBACK_DEL, _cleanup_priv, priv);
|
|
evas_object_image_pixels_get_callback_set(priv->evas_object, NULL, NULL);
|
|
}
|
|
}
|
|
|
|
if (!priv->evas_object)
|
|
goto exit_point;
|
|
|
|
buffer = gst_buffer_ref(send->frame);
|
|
|
|
if (!send->vfmapped)
|
|
{
|
|
if (!gst_buffer_map(buffer, &map, GST_MAP_READ))
|
|
{
|
|
gst_buffer_unref(buffer);
|
|
ERR("Cannot map video buffer for read.\n");
|
|
goto exit_point;
|
|
}
|
|
}
|
|
|
|
INF("sink main render [%i, %i] (source height: %i)", send->info.width, send->eheight, send->info.height);
|
|
|
|
evas_object_image_alpha_set(priv->evas_object, 0);
|
|
evas_object_image_colorspace_set(priv->evas_object, send->eformat);
|
|
evas_object_image_size_set(priv->evas_object, send->info.width, send->eheight);
|
|
|
|
evas_data = evas_object_image_data_get(priv->evas_object, 1);
|
|
if (!evas_data)
|
|
{
|
|
if (!send->vfmapped)
|
|
{
|
|
gst_buffer_unmap(buffer, &map);
|
|
priv->mapped = EINA_FALSE;
|
|
}
|
|
else
|
|
{
|
|
gst_video_frame_unmap(&(send->vframe));
|
|
priv->vfmapped = EINA_FALSE;
|
|
}
|
|
gst_buffer_unref(buffer);
|
|
goto exit_point;
|
|
}
|
|
|
|
// XXX: need to handle GstVideoCropMeta to get video cropping right
|
|
// XXX: can't get crop meta from buffer (always null)
|
|
// GstVideoCropMeta *meta;
|
|
// meta = gst_buffer_get_video_crop_meta(buffer);
|
|
// printf("META: %p\n", meta);
|
|
|
|
/* this just is a demo of broken vaapi back-end values for stride and
|
|
* plane offset - the below is what i needed to fix them up for a few videos
|
|
*/
|
|
/*
|
|
info.stride[0] = 64 * ((send->info.stride[0] + 63) / 64);
|
|
info.stride[1] = 64 * ((send->info.stride[1] + 63) / 64);
|
|
info.stride[2] = 64 * ((send->info.stride[2] + 63) / 64);
|
|
info.stride[3] = 64 * ((send->info.stride[3] + 63) / 64);
|
|
info.plane_offset[0] = send->info.offset[0];
|
|
info.plane_offset[1] = (((send->info.height + 15) / 16) * 16) * info.stride[1];
|
|
info.plane_offset[2] = send->info.offset[2];
|
|
info.plane_offset[3] = send->info.offset[3];
|
|
*/
|
|
if (send->vfmapped)
|
|
{
|
|
GstVideoFrame *vframe = &(send->vframe);
|
|
|
|
map.data = GST_VIDEO_FRAME_PLANE_DATA(vframe, 0);
|
|
info.bpp[0] = GST_VIDEO_FRAME_COMP_PSTRIDE(vframe, 0);
|
|
info.bpp[1] = GST_VIDEO_FRAME_COMP_PSTRIDE(vframe, 1);
|
|
info.bpp[2] = GST_VIDEO_FRAME_COMP_PSTRIDE(vframe, 2);
|
|
info.bpp[3] = GST_VIDEO_FRAME_COMP_PSTRIDE(vframe, 3);
|
|
info.stride[0] = GST_VIDEO_FRAME_COMP_STRIDE(vframe, 0);
|
|
info.stride[1] = GST_VIDEO_FRAME_COMP_STRIDE(vframe, 1);
|
|
info.stride[2] = GST_VIDEO_FRAME_COMP_STRIDE(vframe, 2);
|
|
info.stride[3] = GST_VIDEO_FRAME_COMP_STRIDE(vframe, 3);
|
|
info.plane_ptr[0] = GST_VIDEO_FRAME_PLANE_DATA(vframe, 0);
|
|
info.plane_ptr[1] = GST_VIDEO_FRAME_PLANE_DATA(vframe, 1);
|
|
info.plane_ptr[2] = GST_VIDEO_FRAME_PLANE_DATA(vframe, 2);
|
|
info.plane_ptr[3] = GST_VIDEO_FRAME_PLANE_DATA(vframe, 3);
|
|
}
|
|
else
|
|
{
|
|
info.bpp[0] = 1;
|
|
info.bpp[1] = 1;
|
|
info.bpp[2] = 1;
|
|
info.bpp[3] = 1;
|
|
info.stride[0] = send->info.stride[0];
|
|
info.stride[1] = send->info.stride[1];
|
|
info.stride[2] = send->info.stride[2];
|
|
info.stride[3] = send->info.stride[3];
|
|
info.plane_ptr[0] = ((unsigned char *)map.data) + send->info.offset[0];
|
|
info.plane_ptr[1] = ((unsigned char *)map.data) + send->info.offset[1];
|
|
info.plane_ptr[2] = ((unsigned char *)map.data) + send->info.offset[2];
|
|
info.plane_ptr[3] = ((unsigned char *)map.data) + send->info.offset[3];
|
|
}
|
|
|
|
if (send->func)
|
|
send->func(evas_data, map.data, send->info.width, send->info.height, send->eheight, &info);
|
|
else
|
|
WRN("No way to decode %x colorspace !", send->eformat);
|
|
|
|
evas_object_image_data_set(priv->evas_object, evas_data);
|
|
evas_object_image_data_update_add(priv->evas_object, 0, 0, send->info.width, send->eheight);
|
|
evas_object_image_pixels_dirty_set(priv->evas_object, 0);
|
|
|
|
_update_emotion_fps(priv);
|
|
|
|
ratio = (double) send->info.width / (double) send->eheight;
|
|
ratio *= (double) send->info.par_n / (double) send->info.par_d;
|
|
|
|
_emotion_frame_resize(priv->emotion_object, send->info.width, send->eheight, ratio);
|
|
|
|
if (priv->vfmapped)
|
|
{
|
|
gst_video_frame_unmap(&(priv->last_vframe));
|
|
}
|
|
else
|
|
{
|
|
if ((priv->mapped) && (priv->last_buffer))
|
|
gst_buffer_unmap(priv->last_buffer, &(priv->map_info));
|
|
}
|
|
if (send->vfmapped)
|
|
{
|
|
priv->last_vframe = send->vframe;
|
|
priv->vfmapped = EINA_TRUE;
|
|
}
|
|
else
|
|
{
|
|
priv->vfmapped = EINA_FALSE;
|
|
priv->map_info = map;
|
|
priv->mapped = EINA_TRUE;
|
|
}
|
|
|
|
if (priv->last_buffer) gst_buffer_unref(priv->last_buffer);
|
|
priv->last_buffer = buffer;
|
|
|
|
_emotion_frame_new(priv->emotion_object);
|
|
|
|
exit_point:
|
|
if (!priv->unlocked)
|
|
eina_condition_signal(&priv->c);
|
|
|
|
eina_lock_release(&priv->m);
|
|
|
|
emotion_gstreamer_buffer_free(send);
|
|
|
|
_emotion_pending_ecore_end();
|
|
}
|
|
|
|
/* Must be called with priv->m taken */
|
|
static void
|
|
unlock_buffer_mutex(EmotionVideoSinkPrivate* priv)
|
|
{
|
|
priv->unlocked = EINA_TRUE;
|
|
|
|
eina_condition_signal(&priv->c);
|
|
}
|
|
|
|
static void
|
|
emotion_video_sink_class_init(EmotionVideoSinkClass* klass)
|
|
{
|
|
GObjectClass* gobject_class;
|
|
GstElementClass* gstelement_class;
|
|
GstBaseSinkClass* gstbase_sink_class;
|
|
GstVideoSinkClass* gstvideo_sink_class;
|
|
|
|
gobject_class = G_OBJECT_CLASS(klass);
|
|
gstelement_class = GST_ELEMENT_CLASS(klass);
|
|
gstbase_sink_class = GST_BASE_SINK_CLASS(klass);
|
|
gstvideo_sink_class = GST_VIDEO_SINK_CLASS(klass);
|
|
|
|
gobject_class->set_property = emotion_video_sink_set_property;
|
|
gobject_class->get_property = emotion_video_sink_get_property;
|
|
|
|
g_object_class_install_property (gobject_class, PROP_EMOTION_OBJECT,
|
|
g_param_spec_pointer ("emotion-object", "Emotion Object",
|
|
"The Emotion object where the display of the video will be done",
|
|
G_PARAM_READWRITE));
|
|
|
|
gobject_class->dispose = emotion_video_sink_dispose;
|
|
|
|
gst_element_class_add_pad_template(gstelement_class, gst_static_pad_template_get(&sinktemplate));
|
|
gst_element_class_set_static_metadata(gstelement_class, "Emotion video sink",
|
|
"Sink/Video", "Sends video data from a GStreamer pipeline to an Emotion object",
|
|
"Vincent Torri <vtorri@univ-evry.fr>");
|
|
|
|
gstbase_sink_class->set_caps = emotion_video_sink_set_caps;
|
|
gstbase_sink_class->stop = emotion_video_sink_stop;
|
|
gstbase_sink_class->start = emotion_video_sink_start;
|
|
gstbase_sink_class->unlock = emotion_video_sink_unlock;
|
|
gstbase_sink_class->unlock_stop = emotion_video_sink_unlock_stop;
|
|
gstvideo_sink_class->show_frame = emotion_video_sink_show_frame;
|
|
}
|
|
|
|
gboolean
|
|
gstreamer_plugin_init (GstPlugin * plugin)
|
|
{
|
|
return gst_element_register (plugin,
|
|
"emotion-sink",
|
|
GST_RANK_NONE,
|
|
EMOTION_TYPE_VIDEO_SINK);
|
|
}
|
|
|