| /* GStreamer |
| * Copyright (C) 2012 Roland Krikava <info@bluedigits.com> |
| * Copyright (C) 2010-2011 David Hoyt <dhoyt@hoytsoft.org> |
| * Copyright (C) 2010 Andoni Morales <ylatuya@gmail.com> |
| * Copyright (C) 2012 Sebastian Dröge <sebastian.droege@collabora.co.uk> |
| * |
| * 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., 59 Temple Place - Suite 330, |
| * Boston, MA 02111-1307, USA. |
| */ |
| #ifdef HAVE_CONFIG_H |
| #include "config.h" |
| #endif |
| |
| #include "d3dvideosink.h" |
| |
| #define ELEMENT_NAME "d3dvideosink" |
| |
| enum |
| { |
| PROP_0, |
| PROP_FORCE_ASPECT_RATIO, |
| PROP_CREATE_RENDER_WINDOW, |
| PROP_STREAM_STOP_ON_CLOSE, |
| PROP_ENABLE_NAVIGATION_EVENTS, |
| PROP_LAST |
| }; |
| |
| #define DEFAULT_FORCE_ASPECT_RATIO TRUE |
| #define DEFAULT_CREATE_RENDER_WINDOW TRUE |
| #define DEFAULT_STREAM_STOP_ON_CLOSE TRUE |
| #define DEFAULT_ENABLE_NAVIGATION_EVENTS TRUE |
| |
| static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", |
| GST_PAD_SINK, |
| GST_PAD_ALWAYS, |
| GST_STATIC_CAPS ("video/x-raw, " |
| "format = (string) { I420, YV12, UYVY, YUY2, NV12, BGRx, RGBx, RGBA, BGRA, BGR, RGB16, RGB15 }, " |
| "framerate = (fraction) [ 0, MAX ], " |
| "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]") |
| ); |
| |
| GST_DEBUG_CATEGORY (gst_d3dvideosink_debug); |
| #define GST_CAT_DEFAULT gst_d3dvideosink_debug |
| |
| /* FWD DECLS */ |
| /* GstXOverlay Interface */ |
| static void |
| gst_d3dvideosink_video_overlay_interface_init (GstVideoOverlayInterface * |
| iface); |
| static void gst_d3dvideosink_set_window_handle (GstVideoOverlay * overlay, |
| guintptr window_id); |
| static void gst_d3dvideosink_set_render_rectangle (GstVideoOverlay * overlay, |
| gint x, gint y, gint width, gint height); |
| static void gst_d3dvideosink_expose (GstVideoOverlay * overlay); |
| /* GstNavigation Interface */ |
| static void gst_d3dvideosink_navigation_interface_init (GstNavigationInterface * |
| iface); |
| static void gst_d3dvideosink_navigation_send_event (GstNavigation * navigation, |
| GstStructure * structure); |
| /* GObject */ |
| static void gst_d3dvideosink_set_property (GObject * object, guint prop_id, |
| const GValue * value, GParamSpec * pspec); |
| static void gst_d3dvideosink_get_property (GObject * object, guint prop_id, |
| GValue * value, GParamSpec * pspec); |
| static void gst_d3dvideosink_finalize (GObject * gobject); |
| /* GstBaseSink */ |
| static GstCaps *gst_d3dvideosink_get_caps (GstBaseSink * basesink, |
| GstCaps * filter); |
| static gboolean gst_d3dvideosink_set_caps (GstBaseSink * bsink, GstCaps * caps); |
| static gboolean gst_d3dvideosink_start (GstBaseSink * sink); |
| static gboolean gst_d3dvideosink_stop (GstBaseSink * sink); |
| static gboolean gst_d3dvideosink_propose_allocation (GstBaseSink * bsink, |
| GstQuery * query); |
| /* GstVideoSink */ |
| static GstFlowReturn gst_d3dvideosink_show_frame (GstVideoSink * vsink, |
| GstBuffer * buffer); |
| |
| #define _do_init \ |
| G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION, gst_d3dvideosink_navigation_interface_init); \ |
| G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_OVERLAY, gst_d3dvideosink_video_overlay_interface_init); \ |
| GST_DEBUG_CATEGORY_INIT (gst_d3dvideosink_debug, ELEMENT_NAME, 0, "Direct3D Video"); |
| |
| G_DEFINE_TYPE_WITH_CODE (GstD3DVideoSink, gst_d3dvideosink, GST_TYPE_VIDEO_SINK, |
| _do_init); |
| |
| static void |
| gst_d3dvideosink_class_init (GstD3DVideoSinkClass * klass) |
| { |
| GObjectClass *gobject_class; |
| GstElementClass *gstelement_class; |
| GstVideoSinkClass *gstvideosink_class; |
| GstBaseSinkClass *gstbasesink_class; |
| |
| gobject_class = (GObjectClass *) klass; |
| gstelement_class = (GstElementClass *) klass; |
| gstvideosink_class = (GstVideoSinkClass *) klass; |
| gstbasesink_class = (GstBaseSinkClass *) klass; |
| |
| gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_d3dvideosink_finalize); |
| gobject_class->set_property = |
| GST_DEBUG_FUNCPTR (gst_d3dvideosink_set_property); |
| gobject_class->get_property = |
| GST_DEBUG_FUNCPTR (gst_d3dvideosink_get_property); |
| |
| gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_d3dvideosink_get_caps); |
| gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_d3dvideosink_set_caps); |
| gstbasesink_class->start = GST_DEBUG_FUNCPTR (gst_d3dvideosink_start); |
| gstbasesink_class->stop = GST_DEBUG_FUNCPTR (gst_d3dvideosink_stop); |
| gstbasesink_class->propose_allocation = |
| GST_DEBUG_FUNCPTR (gst_d3dvideosink_propose_allocation); |
| |
| gstvideosink_class->show_frame = |
| GST_DEBUG_FUNCPTR (gst_d3dvideosink_show_frame); |
| |
| /* Add properties */ |
| g_object_class_install_property (G_OBJECT_CLASS (klass), |
| PROP_FORCE_ASPECT_RATIO, g_param_spec_boolean ("force-aspect-ratio", |
| "Force aspect ratio", |
| "When enabled, scaling will respect original aspect ratio", |
| DEFAULT_FORCE_ASPECT_RATIO, |
| (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
| g_object_class_install_property (G_OBJECT_CLASS (klass), |
| PROP_CREATE_RENDER_WINDOW, g_param_spec_boolean ("create-render-window", |
| "Create render window", |
| "If no window ID is given, a new render window is created", |
| DEFAULT_CREATE_RENDER_WINDOW, |
| (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
| g_object_class_install_property (G_OBJECT_CLASS (klass), |
| PROP_STREAM_STOP_ON_CLOSE, g_param_spec_boolean ("stream-stop-on-close", |
| "Stop streaming on window close", |
| "If the render window is closed stop stream", |
| DEFAULT_STREAM_STOP_ON_CLOSE, |
| (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
| g_object_class_install_property (G_OBJECT_CLASS (klass), |
| PROP_ENABLE_NAVIGATION_EVENTS, |
| g_param_spec_boolean ("enable-navigation-events", |
| "Enable navigation events", |
| "When enabled, navigation events are sent upstream", |
| DEFAULT_ENABLE_NAVIGATION_EVENTS, |
| (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
| |
| gst_element_class_set_static_metadata (gstelement_class, |
| "Direct3D video sink", "Sink/Video", |
| "Display data using a Direct3D video renderer", |
| "David Hoyt <dhoyt@hoytsoft.org>, Roland Krikava <info@bluedigits.com>"); |
| |
| gst_element_class_add_static_pad_template (gstelement_class, &sink_template); |
| |
| g_rec_mutex_init (&klass->lock); |
| } |
| |
| static void |
| gst_d3dvideosink_init (GstD3DVideoSink * sink) |
| { |
| GST_DEBUG_OBJECT (sink, " "); |
| |
| /* Init Properties */ |
| sink->force_aspect_ratio = DEFAULT_FORCE_ASPECT_RATIO; |
| sink->create_internal_window = DEFAULT_CREATE_RENDER_WINDOW; |
| sink->stream_stop_on_close = DEFAULT_STREAM_STOP_ON_CLOSE; |
| sink->enable_navigation_events = DEFAULT_ENABLE_NAVIGATION_EVENTS; |
| sink->d3d.surface = NULL; |
| |
| g_rec_mutex_init (&sink->lock); |
| } |
| |
| /* GObject Functions */ |
| |
| static void |
| gst_d3dvideosink_finalize (GObject * gobject) |
| { |
| GstD3DVideoSink *sink = GST_D3DVIDEOSINK (gobject); |
| |
| GST_DEBUG_OBJECT (sink, " "); |
| |
| gst_object_replace ((GstObject **) & sink->pool, NULL); |
| gst_object_replace ((GstObject **) & sink->fallback_pool, NULL); |
| |
| gst_caps_replace (&sink->supported_caps, NULL); |
| |
| g_rec_mutex_clear (&sink->lock); |
| |
| G_OBJECT_CLASS (gst_d3dvideosink_parent_class)->finalize (gobject); |
| } |
| |
| static void |
| gst_d3dvideosink_set_property (GObject * object, guint prop_id, |
| const GValue * value, GParamSpec * pspec) |
| { |
| GstD3DVideoSink *sink = GST_D3DVIDEOSINK (object); |
| |
| switch (prop_id) { |
| case PROP_FORCE_ASPECT_RATIO: |
| sink->force_aspect_ratio = g_value_get_boolean (value); |
| break; |
| case PROP_CREATE_RENDER_WINDOW: |
| sink->create_internal_window = g_value_get_boolean (value); |
| break; |
| case PROP_STREAM_STOP_ON_CLOSE: |
| sink->stream_stop_on_close = g_value_get_boolean (value); |
| break; |
| case PROP_ENABLE_NAVIGATION_EVENTS: |
| sink->enable_navigation_events = g_value_get_boolean (value); |
| break; |
| default: |
| G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
| break; |
| } |
| } |
| |
| static void |
| gst_d3dvideosink_get_property (GObject * object, guint prop_id, GValue * value, |
| GParamSpec * pspec) |
| { |
| GstD3DVideoSink *sink = GST_D3DVIDEOSINK (object); |
| |
| switch (prop_id) { |
| case PROP_FORCE_ASPECT_RATIO: |
| g_value_set_boolean (value, sink->force_aspect_ratio); |
| break; |
| case PROP_CREATE_RENDER_WINDOW: |
| g_value_set_boolean (value, sink->create_internal_window); |
| break; |
| case PROP_STREAM_STOP_ON_CLOSE: |
| g_value_set_boolean (value, sink->stream_stop_on_close); |
| break; |
| case PROP_ENABLE_NAVIGATION_EVENTS: |
| g_value_set_boolean (value, sink->enable_navigation_events); |
| break; |
| default: |
| G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
| break; |
| } |
| } |
| |
| /* GstBaseSinkClass Functions */ |
| |
| static GstCaps * |
| gst_d3dvideosink_get_caps (GstBaseSink * basesink, GstCaps * filter) |
| { |
| GstD3DVideoSink *sink = GST_D3DVIDEOSINK (basesink); |
| GstCaps *caps; |
| |
| caps = d3d_supported_caps (sink); |
| if (!caps) |
| caps = gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD (sink)); |
| |
| if (caps && filter) { |
| GstCaps *isect; |
| isect = gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST); |
| gst_caps_unref (caps); |
| caps = isect; |
| } |
| |
| return caps; |
| } |
| |
| static gboolean |
| gst_d3dvideosink_set_caps (GstBaseSink * bsink, GstCaps * caps) |
| { |
| GstD3DVideoSink *sink; |
| GstCaps *sink_caps; |
| gint video_width, video_height; |
| gint video_par_n, video_par_d; /* video's PAR */ |
| gint display_par_n = 1, display_par_d = 1; /* display's PAR */ |
| guint num, den; |
| GstBufferPool *newpool, *oldpool; |
| GstBufferPool *newfbpool, *oldfbpool; |
| GstStructure *config; |
| |
| GST_DEBUG_OBJECT (bsink, "Caps: %" GST_PTR_FORMAT, caps); |
| sink = GST_D3DVIDEOSINK (bsink); |
| |
| sink_caps = d3d_supported_caps (sink); |
| |
| if (!gst_caps_can_intersect (sink_caps, caps)) |
| goto incompatible_caps; |
| gst_caps_replace (&sink_caps, NULL); |
| |
| memset (&sink->info, 0, sizeof (GstVideoInfo)); |
| if (!gst_video_info_from_caps (&sink->info, caps)) |
| goto invalid_format; |
| |
| sink->format = sink->info.finfo->format; |
| video_width = sink->info.width; |
| video_height = sink->info.height; |
| video_par_n = sink->info.par_n; |
| video_par_d = sink->info.par_d; |
| |
| GST_DEBUG_OBJECT (bsink, "Set Caps Format: %s", |
| gst_video_format_to_string (sink->format)); |
| |
| /* get aspect ratio from caps if it's present, and |
| * convert video width and height to a display width and height |
| * using wd / hd = wv / hv * PARv / PARd */ |
| |
| /* TODO: Get display PAR */ |
| |
| if (!gst_video_calculate_display_ratio (&num, &den, video_width, |
| video_height, video_par_n, video_par_d, display_par_n, display_par_d)) |
| goto no_disp_ratio; |
| |
| GST_DEBUG_OBJECT (sink, |
| "video width/height: %dx%d, calculated display ratio: %d/%d format: %u", |
| video_width, video_height, num, den, sink->format); |
| |
| /* now find a width x height that respects this display ratio. |
| * prefer those that have one of w/h the same as the incoming video |
| * using wd / hd = num / den |
| */ |
| |
| /* start with same height, because of interlaced video |
| * check hd / den is an integer scale factor, and scale wd with the PAR |
| */ |
| if (video_height % den == 0) { |
| GST_DEBUG_OBJECT (sink, "keeping video height"); |
| GST_VIDEO_SINK_WIDTH (sink) = (guint) |
| gst_util_uint64_scale_int (video_height, num, den); |
| GST_VIDEO_SINK_HEIGHT (sink) = video_height; |
| } else if (video_width % num == 0) { |
| GST_DEBUG_OBJECT (sink, "keeping video width"); |
| GST_VIDEO_SINK_WIDTH (sink) = video_width; |
| GST_VIDEO_SINK_HEIGHT (sink) = (guint) |
| gst_util_uint64_scale_int (video_width, den, num); |
| } else { |
| GST_DEBUG_OBJECT (sink, "approximating while keeping video height"); |
| GST_VIDEO_SINK_WIDTH (sink) = (guint) |
| gst_util_uint64_scale_int (video_height, num, den); |
| GST_VIDEO_SINK_HEIGHT (sink) = video_height; |
| } |
| GST_DEBUG_OBJECT (sink, "scaling to %dx%d", |
| GST_VIDEO_SINK_WIDTH (sink), GST_VIDEO_SINK_HEIGHT (sink)); |
| |
| if (GST_VIDEO_SINK_WIDTH (sink) <= 0 || GST_VIDEO_SINK_HEIGHT (sink) <= 0) |
| goto no_display_size; |
| |
| memset (&sink->crop_rect, 0, sizeof (sink->crop_rect)); |
| sink->crop_rect.w = sink->info.width; |
| sink->crop_rect.h = sink->info.height; |
| |
| sink->width = video_width; |
| sink->height = video_height; |
| |
| GST_DEBUG_OBJECT (bsink, "Selected caps: %" GST_PTR_FORMAT, caps); |
| |
| if (!d3d_set_render_format (sink)) |
| goto incompatible_caps; |
| |
| /* Create a window (or start using an application-supplied one, then connect the graph */ |
| d3d_prepare_window (sink); |
| |
| newpool = gst_d3dsurface_buffer_pool_new (sink); |
| config = gst_buffer_pool_get_config (newpool); |
| /* we need at least 2 buffer because we hold on to the last one */ |
| gst_buffer_pool_config_set_params (config, caps, sink->info.size, 2, 0); |
| if (!gst_buffer_pool_set_config (newpool, config)) { |
| gst_object_unref (newpool); |
| GST_ERROR_OBJECT (sink, "Failed to set buffer pool configuration"); |
| return FALSE; |
| } |
| |
| newfbpool = gst_d3dsurface_buffer_pool_new (sink); |
| config = gst_buffer_pool_get_config (newfbpool); |
| /* we need at least 2 buffer because we hold on to the last one */ |
| gst_buffer_pool_config_set_params (config, caps, sink->info.size, 2, 0); |
| /* Fallback pool must use videometa */ |
| gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META); |
| if (!gst_buffer_pool_set_config (newfbpool, config)) { |
| gst_object_unref (newfbpool); |
| GST_ERROR_OBJECT (sink, "Failed to set buffer pool configuration"); |
| return FALSE; |
| } |
| |
| GST_OBJECT_LOCK (sink); |
| oldpool = sink->pool; |
| sink->pool = newpool; |
| oldfbpool = sink->fallback_pool; |
| sink->fallback_pool = newfbpool; |
| GST_OBJECT_UNLOCK (sink); |
| |
| if (oldpool) |
| gst_object_unref (oldpool); |
| if (oldfbpool) { |
| gst_buffer_pool_set_active (oldfbpool, FALSE); |
| gst_object_unref (oldfbpool); |
| } |
| |
| return TRUE; |
| /* ERRORS */ |
| incompatible_caps: |
| { |
| GST_ERROR_OBJECT (sink, "caps incompatible"); |
| gst_caps_unref (sink_caps); |
| return FALSE; |
| } |
| invalid_format: |
| { |
| GST_DEBUG_OBJECT (sink, |
| "Could not locate image format from caps %" GST_PTR_FORMAT, caps); |
| return FALSE; |
| } |
| no_disp_ratio: |
| { |
| GST_ELEMENT_ERROR (sink, CORE, NEGOTIATION, (NULL), |
| ("Error calculating the output display ratio of the video.")); |
| return FALSE; |
| } |
| no_display_size: |
| { |
| GST_ELEMENT_ERROR (sink, CORE, NEGOTIATION, (NULL), |
| ("Error calculating the output display ratio of the video.")); |
| return FALSE; |
| } |
| } |
| |
| static gboolean |
| gst_d3dvideosink_start (GstBaseSink * bsink) |
| { |
| GstD3DVideoSink *sink = GST_D3DVIDEOSINK (bsink); |
| |
| GST_DEBUG_OBJECT (bsink, "Start() called"); |
| |
| return d3d_class_init (sink); |
| } |
| |
| static gboolean |
| gst_d3dvideosink_stop (GstBaseSink * bsink) |
| { |
| GstD3DVideoSink *sink = GST_D3DVIDEOSINK (bsink); |
| |
| GST_DEBUG_OBJECT (bsink, "Stop() called"); |
| d3d_stop (sink); |
| d3d_class_destroy (sink); |
| |
| return TRUE; |
| } |
| |
| static gboolean |
| gst_d3dvideosink_propose_allocation (GstBaseSink * bsink, GstQuery * query) |
| { |
| GstD3DVideoSink *sink = GST_D3DVIDEOSINK (bsink); |
| GstBufferPool *pool; |
| GstStructure *config; |
| GstCaps *caps; |
| guint size; |
| gboolean need_pool; |
| |
| gst_query_parse_allocation (query, &caps, &need_pool); |
| if (!caps) { |
| GST_DEBUG_OBJECT (sink, "no caps specified"); |
| return FALSE; |
| } |
| |
| gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL); |
| gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL); |
| |
| #ifdef DISABLE_BUFFER_POOL |
| return TRUE; |
| #endif |
| |
| /* FIXME re-using buffer pool breaks renegotiation */ |
| GST_OBJECT_LOCK (sink); |
| pool = sink->pool ? gst_object_ref (sink->pool) : NULL; |
| GST_OBJECT_UNLOCK (sink); |
| |
| if (pool) { |
| GstCaps *pcaps; |
| |
| /* we had a pool, check caps */ |
| GST_DEBUG_OBJECT (sink, "check existing pool caps"); |
| config = gst_buffer_pool_get_config (pool); |
| gst_buffer_pool_config_get_params (config, &pcaps, &size, NULL, NULL); |
| |
| if (!gst_caps_is_equal (caps, pcaps)) { |
| GST_DEBUG_OBJECT (sink, "pool has different caps"); |
| /* different caps, we can't use this pool */ |
| gst_object_unref (pool); |
| pool = NULL; |
| } |
| gst_structure_free (config); |
| } else { |
| GstVideoInfo info; |
| |
| if (!gst_video_info_from_caps (&info, caps)) { |
| GST_ERROR_OBJECT (sink, "allocation query has invalid caps %" |
| GST_PTR_FORMAT, caps); |
| return FALSE; |
| } |
| |
| /* the normal size of a frame */ |
| size = info.size; |
| } |
| |
| if (pool == NULL && need_pool) { |
| GST_DEBUG_OBJECT (sink, "create new pool"); |
| pool = gst_d3dsurface_buffer_pool_new (sink); |
| |
| config = gst_buffer_pool_get_config (pool); |
| /* we need at least 2 buffer because we hold on to the last one */ |
| gst_buffer_pool_config_set_params (config, caps, size, 2, 0); |
| if (!gst_buffer_pool_set_config (pool, config)) { |
| gst_object_unref (pool); |
| GST_ERROR_OBJECT (sink, "failed to set pool configuration"); |
| return FALSE; |
| } |
| } |
| |
| /* we need at least 2 buffer because we hold on to the last one */ |
| gst_query_add_allocation_pool (query, pool, size, 2, 0); |
| if (pool) |
| gst_object_unref (pool); |
| |
| return TRUE; |
| } |
| |
| /* PUBLIC FUNCTIONS */ |
| |
| /* Iterface Registrations */ |
| |
| static void |
| gst_d3dvideosink_video_overlay_interface_init (GstVideoOverlayInterface * iface) |
| { |
| iface->set_window_handle = gst_d3dvideosink_set_window_handle; |
| iface->set_render_rectangle = gst_d3dvideosink_set_render_rectangle; |
| iface->expose = gst_d3dvideosink_expose; |
| } |
| |
| static void |
| gst_d3dvideosink_navigation_interface_init (GstNavigationInterface * iface) |
| { |
| iface->send_event = gst_d3dvideosink_navigation_send_event; |
| } |
| |
| /* Video Render Code */ |
| |
| static void |
| gst_d3dvideosink_set_window_handle (GstVideoOverlay * overlay, |
| guintptr window_id) |
| { |
| d3d_set_window_handle (GST_D3DVIDEOSINK (overlay), window_id, FALSE); |
| } |
| |
| static void |
| gst_d3dvideosink_set_render_rectangle (GstVideoOverlay * overlay, gint x, |
| gint y, gint width, gint height) |
| { |
| GstD3DVideoSink *sink = GST_D3DVIDEOSINK (overlay); |
| sink->render_rect.x = x; |
| sink->render_rect.y = y; |
| sink->render_rect.w = width; |
| sink->render_rect.h = height; |
| d3d_set_render_rectangle (sink); |
| } |
| |
| static void |
| gst_d3dvideosink_expose (GstVideoOverlay * overlay) |
| { |
| GstD3DVideoSink *sink = GST_D3DVIDEOSINK (overlay); |
| d3d_expose_window (sink); |
| } |
| |
| static GstFlowReturn |
| gst_d3dvideosink_show_frame (GstVideoSink * vsink, GstBuffer * buffer) |
| { |
| GstD3DVideoSink *sink = GST_D3DVIDEOSINK (vsink); |
| return d3d_render_buffer (sink, buffer); |
| } |
| |
| /* Video Navigation Events */ |
| |
| static void |
| gst_d3dvideosink_navigation_send_event (GstNavigation * navigation, |
| GstStructure * structure) |
| { |
| GstD3DVideoSink *sink = GST_D3DVIDEOSINK (navigation); |
| GstEvent *e; |
| |
| if ((e = gst_event_new_navigation (structure))) { |
| GstPad *pad; |
| if ((pad = gst_pad_get_peer (GST_VIDEO_SINK_PAD (sink)))) { |
| if (!gst_pad_send_event (pad, gst_event_ref (e))) { |
| /* If upstream didn't handle the event we'll post a message with it |
| * for the application in case it wants to do something with it */ |
| gst_element_post_message (GST_ELEMENT_CAST (sink), |
| gst_navigation_message_new_event (GST_OBJECT_CAST (sink), e)); |
| } |
| gst_event_unref (e); |
| gst_object_unref (pad); |
| } |
| } |
| } |
| |
| /* PRIVATE FUNCTIONS */ |
| |
| |
| /* Plugin entry point */ |
| static gboolean |
| plugin_init (GstPlugin * plugin) |
| { |
| /* PRIMARY: this is the best videosink to use on windows */ |
| if (!gst_element_register (plugin, ELEMENT_NAME, |
| GST_RANK_PRIMARY, GST_TYPE_D3DVIDEOSINK)) |
| return FALSE; |
| |
| return TRUE; |
| } |
| |
| GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, |
| GST_VERSION_MINOR, |
| d3d, |
| "Direct3D plugin", |
| plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) |