| /* GStreamer |
| * |
| * Copyright (C) 2011 Collabora Ltda |
| * Copyright (C) 2011 Texas Instruments |
| * @author: Luciana Fujii Pontello <luciana.fujii@collabora.co.uk> |
| * @author: Edward Hervey <edward@collabora.com> |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser 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. |
| */ |
| |
| /* Object header */ |
| #include "gstpvrvideosink.h" |
| #include "gstpvrbufferpool.h" |
| |
| #include <gst/video/gstvideosink.h> |
| #include <gst/video/videooverlay.h> |
| /* Helper functions */ |
| #include <gst/video/gstvideometa.h> |
| |
| /* Debugging category */ |
| #include <gst/gstinfo.h> |
| |
| #define LINUX |
| #include <dri2_ws.h> |
| #include <services.h> |
| #include <img_defs.h> |
| #include <servicesext.h> |
| |
| #define DEFAULT_QUEUE_SIZE 12 |
| |
| GST_DEBUG_CATEGORY_EXTERN (gst_debug_pvrvideosink); |
| #define GST_CAT_DEFAULT gst_debug_pvrvideosink |
| |
| #define PVR2DMEMINFO_INITIALISE(d, s) \ |
| { \ |
| (d)->hPrivateData = (IMG_VOID *)(s); \ |
| (d)->hPrivateMapData = (IMG_VOID *)(s->hKernelMemInfo); \ |
| (d)->ui32DevAddr = (IMG_UINT32) (s)->sDevVAddr.uiAddr; \ |
| (d)->ui32MemSize = (s)->uAllocSize; \ |
| (d)->pBase = (s)->pvLinAddr;\ |
| (d)->ulFlags = (s)->ui32Flags;\ |
| } |
| |
| /* end of internal definitions */ |
| |
| static void gst_pvrvideosink_reset (GstPVRVideoSink * pvrvideosink); |
| static void gst_pvrvideosink_xwindow_draw_borders (GstPVRVideoSink * |
| pvrvideosink, GstXWindow * xwindow, GstVideoRectangle rect); |
| static void gst_pvrvideosink_expose (GstVideoOverlay * overlay); |
| static void gst_pvrvideosink_xwindow_destroy (GstPVRVideoSink * pvrvideosink, |
| GstXWindow * xwindow); |
| static void gst_pvrvideosink_dcontext_free (GstDrawContext * dcontext); |
| |
| static void gst_pvrvideosink_videooverlay_init (GstVideoOverlayInterface * |
| iface); |
| |
| #define gst_pvrvideosink_parent_class parent_class |
| G_DEFINE_TYPE_WITH_CODE (GstPVRVideoSink, gst_pvrvideosink, GST_TYPE_VIDEO_SINK, |
| G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_OVERLAY, |
| gst_pvrvideosink_videooverlay_init)); |
| |
| |
| static GstStaticPadTemplate gst_pvrvideosink_sink_template_factory = |
| GST_STATIC_PAD_TEMPLATE ("sink", |
| GST_PAD_SINK, |
| GST_PAD_ALWAYS, |
| GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("NV12"))); |
| |
| enum |
| { |
| PROP_0, |
| PROP_FORCE_ASPECT_RATIO, |
| PROP_WINDOW_WIDTH, |
| PROP_WINDOW_HEIGHT |
| }; |
| |
| /* ============================================================= */ |
| /* */ |
| /* Private Methods */ |
| /* */ |
| /* ============================================================= */ |
| |
| /* pvrvideo buffers */ |
| |
| static void |
| gst_pvrvideosink_xwindow_update_geometry (GstPVRVideoSink * pvrvideosink) |
| { |
| XWindowAttributes attr; |
| WSEGLError glerror; |
| WSEGLDrawableParams source_params; |
| PVRSRV_CLIENT_MEM_INFO *client_mem_info; |
| |
| /* Update the window geometry */ |
| g_mutex_lock (pvrvideosink->dcontext->x_lock); |
| if (G_UNLIKELY (pvrvideosink->xwindow == NULL)) { |
| g_mutex_unlock (pvrvideosink->dcontext->x_lock); |
| return; |
| } |
| pvrvideosink->redraw_borders = TRUE; |
| |
| XGetWindowAttributes (pvrvideosink->dcontext->x_display, |
| pvrvideosink->xwindow->window, &attr); |
| |
| pvrvideosink->xwindow->width = attr.width; |
| pvrvideosink->xwindow->height = attr.height; |
| |
| if (!pvrvideosink->have_render_rect) { |
| pvrvideosink->render_rect.x = pvrvideosink->render_rect.y = 0; |
| pvrvideosink->render_rect.w = attr.width; |
| pvrvideosink->render_rect.h = attr.height; |
| } |
| if (pvrvideosink->dcontext != NULL) { |
| glerror = |
| pvrvideosink->dcontext->wsegl_table-> |
| pfnWSEGL_DeleteDrawable (pvrvideosink->dcontext->drawable_handle); |
| if (glerror != WSEGL_SUCCESS) { |
| GST_ERROR_OBJECT (pvrvideosink, "Error destroying drawable"); |
| return; |
| } |
| glerror = |
| pvrvideosink->dcontext->wsegl_table-> |
| pfnWSEGL_CreateWindowDrawable (pvrvideosink->dcontext->display_handle, |
| pvrvideosink->dcontext->glconfig, |
| &pvrvideosink->dcontext->drawable_handle, |
| (NativeWindowType) pvrvideosink->xwindow->window, |
| &pvrvideosink->dcontext->rotation); |
| if (glerror != WSEGL_SUCCESS) { |
| GST_ERROR_OBJECT (pvrvideosink, "Error creating drawable"); |
| return; |
| } |
| glerror = |
| pvrvideosink->dcontext->wsegl_table-> |
| pfnWSEGL_GetDrawableParameters (pvrvideosink->dcontext->drawable_handle, |
| &source_params, &pvrvideosink->render_params); |
| if (glerror != WSEGL_SUCCESS) { |
| GST_ERROR_OBJECT (pvrvideosink, "Error getting Drawable params"); |
| return; |
| } |
| |
| client_mem_info = |
| (PVRSRV_CLIENT_MEM_INFO *) pvrvideosink->render_params.hPrivateData; |
| PVR2DMEMINFO_INITIALISE (&pvrvideosink->dcontext->dst_mem, client_mem_info); |
| } |
| |
| g_mutex_unlock (pvrvideosink->dcontext->x_lock); |
| } |
| |
| /* This function handles XEvents that might be in the queue. It generates |
| GstEvent that will be sent upstream in the pipeline to handle interactivity |
| and navigation. It will also listen for configure events on the window to |
| trigger caps renegotiation so on the fly software scaling can work. */ |
| static void |
| gst_pvrvideosink_handle_xevents (GstPVRVideoSink * pvrvideosink) |
| { |
| XEvent e; |
| gboolean exposed = FALSE; |
| gboolean configured = FALSE; |
| |
| g_mutex_lock (pvrvideosink->flow_lock); |
| g_mutex_lock (pvrvideosink->dcontext->x_lock); |
| |
| /* Handle Expose */ |
| while (XCheckWindowEvent (pvrvideosink->dcontext->x_display, |
| pvrvideosink->xwindow->window, ExposureMask | StructureNotifyMask, |
| &e)) { |
| switch (e.type) { |
| case Expose: |
| exposed = TRUE; |
| break; |
| case ConfigureNotify: |
| g_mutex_unlock (pvrvideosink->dcontext->x_lock); |
| gst_pvrvideosink_xwindow_update_geometry (pvrvideosink); |
| g_mutex_lock (pvrvideosink->dcontext->x_lock); |
| configured = TRUE; |
| break; |
| default: |
| break; |
| } |
| } |
| |
| if (exposed || configured) { |
| g_mutex_unlock (pvrvideosink->dcontext->x_lock); |
| g_mutex_unlock (pvrvideosink->flow_lock); |
| |
| gst_pvrvideosink_expose (GST_VIDEO_OVERLAY (pvrvideosink)); |
| |
| g_mutex_lock (pvrvideosink->flow_lock); |
| g_mutex_lock (pvrvideosink->dcontext->x_lock); |
| } |
| |
| /* Handle Display events */ |
| while (XPending (pvrvideosink->dcontext->x_display)) { |
| XNextEvent (pvrvideosink->dcontext->x_display, &e); |
| |
| switch (e.type) { |
| case ClientMessage:{ |
| Atom wm_delete; |
| |
| wm_delete = XInternAtom (pvrvideosink->dcontext->x_display, |
| "WM_DELETE_WINDOW", True); |
| if (wm_delete != None && wm_delete == (Atom) e.xclient.data.l[0]) { |
| /* Handle window deletion by posting an error on the bus */ |
| GST_ELEMENT_ERROR (pvrvideosink, RESOURCE, NOT_FOUND, |
| ("Output window was closed"), (NULL)); |
| |
| g_mutex_unlock (pvrvideosink->dcontext->x_lock); |
| gst_pvrvideosink_xwindow_destroy (pvrvideosink, |
| pvrvideosink->xwindow); |
| pvrvideosink->xwindow = NULL; |
| g_mutex_lock (pvrvideosink->dcontext->x_lock); |
| } |
| break; |
| } |
| default: |
| break; |
| } |
| } |
| |
| g_mutex_unlock (pvrvideosink->dcontext->x_lock); |
| g_mutex_unlock (pvrvideosink->flow_lock); |
| } |
| |
| static gpointer |
| gst_pvrvideosink_event_thread (GstPVRVideoSink * pvrvideosink) |
| { |
| GST_OBJECT_LOCK (pvrvideosink); |
| while (pvrvideosink->running) { |
| GST_OBJECT_UNLOCK (pvrvideosink); |
| |
| if (pvrvideosink->xwindow) { |
| gst_pvrvideosink_handle_xevents (pvrvideosink); |
| } |
| g_usleep (G_USEC_PER_SEC / 20); |
| |
| GST_OBJECT_LOCK (pvrvideosink); |
| } |
| GST_OBJECT_UNLOCK (pvrvideosink); |
| |
| return NULL; |
| } |
| |
| static void |
| gst_pvrvideosink_manage_event_thread (GstPVRVideoSink * pvrvideosink) |
| { |
| GThread *thread = NULL; |
| |
| /* don't start the thread too early */ |
| if (pvrvideosink->dcontext == NULL) { |
| return; |
| } |
| |
| GST_OBJECT_LOCK (pvrvideosink); |
| if (!pvrvideosink->event_thread) { |
| /* Setup our event listening thread */ |
| GST_DEBUG_OBJECT (pvrvideosink, "run xevent thread"); |
| pvrvideosink->running = TRUE; |
| pvrvideosink->event_thread = g_thread_create ( |
| (GThreadFunc) gst_pvrvideosink_event_thread, pvrvideosink, TRUE, NULL); |
| } |
| GST_OBJECT_UNLOCK (pvrvideosink); |
| |
| /* Wait for our event thread to finish */ |
| if (thread) |
| g_thread_join (thread); |
| } |
| |
| |
| static GstDrawContext * |
| gst_pvrvideosink_get_dcontext (GstPVRVideoSink * pvrvideosink) |
| { |
| GstDrawContext *dcontext = NULL; |
| PVR2DERROR pvr_error; |
| gint refresh_rate; |
| DRI2WSDisplay *displayImpl; |
| WSEGLError glerror; |
| const WSEGLCaps *glcaps; |
| |
| GST_DEBUG_OBJECT (pvrvideosink, "Getting draw context"); |
| |
| dcontext = g_new0 (GstDrawContext, 1); |
| dcontext->x_lock = g_mutex_new (); |
| |
| dcontext->p_blt_info = g_new0 (PVR2D_3DBLT_EXT, 1); |
| if (!dcontext->p_blt_info) |
| goto p_blt_info_alloc_failed; |
| |
| dcontext->p_blt2d_info = g_new0 (PVR2DBLTINFO, 1); |
| |
| GST_LOG_OBJECT (pvrvideosink, "Opening X Display"); |
| dcontext->x_display = XOpenDisplay (NULL); |
| |
| if (dcontext->x_display == NULL) |
| goto fail_open_display; |
| |
| GST_LOG_OBJECT (pvrvideosink, "WSEGL_GetFunctionTablePointer()"); |
| dcontext->wsegl_table = WSEGL_GetFunctionTablePointer (); |
| |
| GST_LOG_OBJECT (pvrvideosink, "pfnWSEGL_IsDisplayValid()"); |
| glerror = dcontext->wsegl_table->pfnWSEGL_IsDisplayValid ( |
| (NativeDisplayType) dcontext->x_display); |
| |
| if (glerror != WSEGL_SUCCESS) |
| goto display_invalid; |
| |
| GST_LOG_OBJECT (pvrvideosink, "pfnWSEGL_InitialiseDisplay()"); |
| |
| glerror = dcontext->wsegl_table->pfnWSEGL_InitialiseDisplay ( |
| (NativeDisplayType) dcontext->x_display, &dcontext->display_handle, |
| &glcaps, &dcontext->glconfig); |
| if (glerror != WSEGL_SUCCESS) |
| goto display_init_failed; |
| |
| displayImpl = (DRI2WSDisplay *) dcontext->display_handle; |
| dcontext->pvr_context = displayImpl->hContext; |
| |
| GST_LOG_OBJECT (pvrvideosink, "PVR2DGetScreenMode()"); |
| |
| pvr_error = PVR2DGetScreenMode (dcontext->pvr_context, |
| &dcontext->display_format, &dcontext->display_width, |
| &dcontext->display_height, &dcontext->stride, &refresh_rate); |
| if (pvr_error != PVR2D_OK) |
| goto screen_mode_failed; |
| |
| GST_DEBUG_OBJECT (pvrvideosink, |
| "Got format:%d, width:%d, height:%d, stride:%d, refresh_rate:%d", |
| dcontext->display_format, dcontext->display_width, |
| dcontext->display_height, dcontext->stride, refresh_rate); |
| |
| dcontext->screen_num = DefaultScreen (dcontext->x_display); |
| dcontext->black = XBlackPixel (dcontext->x_display, dcontext->screen_num); |
| |
| GST_DEBUG_OBJECT (pvrvideosink, "Returning dcontext %p", dcontext); |
| |
| return dcontext; |
| |
| p_blt_info_alloc_failed: |
| { |
| GST_ERROR_OBJECT (pvrvideosink, "Alloc of bltinfo failed"); |
| gst_pvrvideosink_dcontext_free (dcontext); |
| return NULL; |
| } |
| |
| fail_open_display: |
| { |
| GST_ERROR_OBJECT (pvrvideosink, "Failed to open X Display"); |
| gst_pvrvideosink_dcontext_free (dcontext); |
| return NULL; |
| } |
| |
| display_invalid: |
| { |
| GST_ERROR_OBJECT (pvrvideosink, "Display is not valid (glerror:%d)", |
| glerror); |
| gst_pvrvideosink_dcontext_free (dcontext); |
| return NULL; |
| } |
| |
| display_init_failed: |
| { |
| GST_ERROR_OBJECT (pvrvideosink, "Error initializing display (glerror:%d)", |
| glerror); |
| gst_pvrvideosink_dcontext_free (dcontext); |
| return NULL; |
| } |
| |
| screen_mode_failed: |
| { |
| GST_ERROR_OBJECT (pvrvideosink, "Failed to get screen mode. error : %s", |
| gst_pvr2d_error_get_string (pvr_error)); |
| gst_pvrvideosink_dcontext_free (dcontext); |
| return NULL; |
| } |
| } |
| |
| static void |
| gst_pvrvideosink_xwindow_set_title (GstPVRVideoSink * pvrvideosink, |
| GstXWindow * xwindow, const gchar * media_title) |
| { |
| if (media_title) { |
| g_free (pvrvideosink->media_title); |
| pvrvideosink->media_title = g_strdup (media_title); |
| } |
| if (xwindow) { |
| /* we have a window */ |
| if (xwindow->internal) { |
| XTextProperty xproperty; |
| const gchar *app_name; |
| const gchar *title = NULL; |
| gchar *title_mem = NULL; |
| |
| /* set application name as a title */ |
| app_name = g_get_application_name (); |
| |
| if (app_name && pvrvideosink->media_title) { |
| title = title_mem = g_strconcat (pvrvideosink->media_title, " : ", |
| app_name, NULL); |
| } else if (app_name) { |
| title = app_name; |
| } else if (pvrvideosink->media_title) { |
| title = pvrvideosink->media_title; |
| } |
| |
| if (title) { |
| if ((XStringListToTextProperty (((char **) &title), 1, |
| &xproperty)) != 0) { |
| XSetWMName (pvrvideosink->dcontext->x_display, xwindow->window, |
| &xproperty); |
| XFree (xproperty.value); |
| } |
| |
| g_free (title_mem); |
| } |
| } |
| } |
| } |
| |
| static GstXWindow * |
| gst_pvrvideosink_create_window (GstPVRVideoSink * pvrvideosink, gint width, |
| gint height) |
| { |
| WSEGLError glerror; |
| WSEGLDrawableParams source_params; |
| Window root; |
| GstXWindow *xwindow; |
| GstDrawContext *dcontext; |
| XGCValues values; |
| Atom wm_delete; |
| PVRSRV_CLIENT_MEM_INFO *client_mem_info; |
| |
| GST_DEBUG_OBJECT (pvrvideosink, "begin"); |
| |
| dcontext = pvrvideosink->dcontext; |
| xwindow = g_new0 (GstXWindow, 1); |
| |
| xwindow->internal = TRUE; |
| pvrvideosink->render_rect.x = pvrvideosink->render_rect.y = 0; |
| pvrvideosink->render_rect.w = width; |
| pvrvideosink->render_rect.h = height; |
| xwindow->width = width; |
| xwindow->height = height; |
| xwindow->internal = TRUE; |
| |
| g_mutex_lock (pvrvideosink->dcontext->x_lock); |
| |
| root = DefaultRootWindow (dcontext->x_display); |
| xwindow->window = XCreateSimpleWindow (dcontext->x_display, root, 0, 0, |
| width, height, 2, 2, pvrvideosink->dcontext->black); |
| XSelectInput (dcontext->x_display, xwindow->window, |
| ExposureMask | StructureNotifyMask); |
| |
| /* Tell the window manager we'd like delete client messages instead of |
| * being killed */ |
| wm_delete = XInternAtom (pvrvideosink->dcontext->x_display, |
| "WM_DELETE_WINDOW", True); |
| if (wm_delete != None) { |
| (void) XSetWMProtocols (pvrvideosink->dcontext->x_display, xwindow->window, |
| &wm_delete, 1); |
| } |
| |
| XMapWindow (dcontext->x_display, xwindow->window); |
| |
| /* We have to do that to prevent X from redrawing the background on |
| * ConfigureNotify. This takes away flickering of video when resizing. */ |
| XSetWindowBackgroundPixmap (pvrvideosink->dcontext->x_display, |
| xwindow->window, None); |
| |
| gst_pvrvideosink_xwindow_set_title (pvrvideosink, xwindow, NULL); |
| |
| xwindow->gc = XCreateGC (pvrvideosink->dcontext->x_display, |
| xwindow->window, 0, &values); |
| |
| g_mutex_unlock (pvrvideosink->dcontext->x_lock); |
| |
| glerror = |
| dcontext->wsegl_table-> |
| pfnWSEGL_CreateWindowDrawable (dcontext->display_handle, |
| dcontext->glconfig, &(dcontext->drawable_handle), |
| (NativeWindowType) xwindow->window, &(dcontext->rotation)); |
| |
| if (glerror != WSEGL_SUCCESS) { |
| GST_ERROR_OBJECT (pvrvideosink, "Error creating drawable"); |
| return NULL; |
| } |
| glerror = |
| dcontext->wsegl_table-> |
| pfnWSEGL_GetDrawableParameters (dcontext->drawable_handle, &source_params, |
| &pvrvideosink->render_params); |
| client_mem_info = |
| (PVRSRV_CLIENT_MEM_INFO *) pvrvideosink->render_params.hPrivateData; |
| PVR2DMEMINFO_INITIALISE (&dcontext->dst_mem, client_mem_info); |
| |
| GST_DEBUG_OBJECT (pvrvideosink, "end"); |
| return xwindow; |
| } |
| |
| static void |
| gst_pvrvideosink_blit (GstPVRVideoSink * pvrvideosink, GstBuffer * buffer) |
| { |
| PVR2DERROR pvr_error; |
| GstDrawContext *dcontext = pvrvideosink->dcontext; |
| gint video_width; |
| gint video_height; |
| gboolean draw_border = FALSE; |
| PPVR2D_3DBLT_EXT p_blt_3d; |
| PVR2DMEMINFO *src_mem; |
| PVR2DFORMAT pvr_format; |
| GstVideoRectangle result; |
| GstPVRMeta *meta; |
| GstVideoCropMeta *cropmeta; |
| |
| GST_DEBUG_OBJECT (pvrvideosink, "buffer %p", buffer); |
| |
| pvr_format = |
| GST_VIDEO_INFO_FORMAT (&pvrvideosink->info) == |
| GST_VIDEO_FORMAT_NV12 ? PVR2D_YUV420_2PLANE : PVR2D_ARGB8888; |
| |
| g_mutex_lock (pvrvideosink->flow_lock); |
| if (buffer == NULL) |
| buffer = pvrvideosink->current_buffer; |
| |
| if (buffer == NULL) |
| goto done; |
| |
| meta = gst_buffer_get_pvr_meta (buffer); |
| if (G_UNLIKELY (meta == NULL)) |
| goto no_pvr_meta; |
| |
| src_mem = meta->src_mem; |
| p_blt_3d = dcontext->p_blt_info; |
| |
| video_width = GST_VIDEO_SINK_WIDTH (pvrvideosink); |
| video_height = GST_VIDEO_SINK_HEIGHT (pvrvideosink); |
| |
| g_mutex_lock (pvrvideosink->dcontext->x_lock); |
| |
| /* Draw borders when displaying the first frame. After this |
| draw borders only on expose event or after a size change. */ |
| if (!(pvrvideosink->current_buffer) || pvrvideosink->redraw_borders) { |
| draw_border = TRUE; |
| } |
| |
| /* Store a reference to the last image we put, lose the previous one */ |
| if (buffer && pvrvideosink->current_buffer != buffer) { |
| if (pvrvideosink->current_buffer) { |
| GST_LOG_OBJECT (pvrvideosink, "unreffing %p", |
| pvrvideosink->current_buffer); |
| gst_buffer_unref (GST_BUFFER_CAST (pvrvideosink->current_buffer)); |
| } |
| GST_LOG_OBJECT (pvrvideosink, "reffing %p as our current buffer", buffer); |
| pvrvideosink->current_buffer = gst_buffer_ref (buffer); |
| } |
| |
| if (pvrvideosink->keep_aspect) { |
| GstVideoRectangle src = { 0, }; |
| GstVideoRectangle dst = { 0, }; |
| |
| src.w = GST_VIDEO_SINK_WIDTH (pvrvideosink); |
| src.h = GST_VIDEO_SINK_HEIGHT (pvrvideosink); |
| dst.w = pvrvideosink->render_rect.w; |
| dst.h = pvrvideosink->render_rect.h; |
| gst_video_sink_center_rect (src, dst, &result, TRUE); |
| result.x += pvrvideosink->render_rect.x; |
| result.y += pvrvideosink->render_rect.y; |
| } else { |
| memcpy (&result, &pvrvideosink->render_rect, sizeof (GstVideoRectangle)); |
| } |
| |
| p_blt_3d->sDst.pSurfMemInfo = &dcontext->dst_mem; |
| p_blt_3d->sDst.SurfOffset = 0; |
| p_blt_3d->sDst.Stride = 4 * pvrvideosink->render_params.ui32Stride; |
| p_blt_3d->sDst.Format = PVR2D_ARGB8888; |
| p_blt_3d->sDst.SurfWidth = pvrvideosink->xwindow->width; |
| p_blt_3d->sDst.SurfHeight = pvrvideosink->xwindow->height; |
| |
| p_blt_3d->rcDest.left = result.x; |
| p_blt_3d->rcDest.top = result.y; |
| p_blt_3d->rcDest.right = result.w + result.x; |
| p_blt_3d->rcDest.bottom = result.h + result.y; |
| |
| p_blt_3d->sSrc.pSurfMemInfo = src_mem; |
| p_blt_3d->sSrc.SurfOffset = 0; |
| p_blt_3d->sSrc.Stride = GST_VIDEO_INFO_COMP_STRIDE (&pvrvideosink->info, 0); |
| p_blt_3d->sSrc.Format = pvr_format; |
| p_blt_3d->sSrc.SurfWidth = video_width; |
| p_blt_3d->sSrc.SurfHeight = video_height; |
| |
| /* If buffer has crop information, use that */ |
| if ((cropmeta = gst_buffer_get_video_crop_meta (buffer))) { |
| p_blt_3d->rcSource.left = cropmeta->x; |
| p_blt_3d->rcSource.top = cropmeta->y; |
| p_blt_3d->rcSource.right = cropmeta->x + cropmeta->width; |
| p_blt_3d->rcSource.bottom = cropmeta->y + cropmeta->height; |
| } else { |
| p_blt_3d->rcSource.left = 0; |
| p_blt_3d->rcSource.top = 0; |
| p_blt_3d->rcSource.right = video_width; |
| p_blt_3d->rcSource.bottom = video_height; |
| } |
| |
| p_blt_3d->hUseCode = NULL; |
| |
| if (GST_VIDEO_INFO_FORMAT (&pvrvideosink->info) == GST_VIDEO_FORMAT_NV12) |
| p_blt_3d->bDisableDestInput = TRUE; |
| else |
| /* blit fails for RGB without this... not sure why yet... */ |
| p_blt_3d->bDisableDestInput = FALSE; |
| |
| GST_DEBUG_OBJECT (pvrvideosink, "about to blit"); |
| |
| pvr_error = PVR2DBlt3DExt (pvrvideosink->dcontext->pvr_context, |
| dcontext->p_blt_info); |
| |
| if (pvr_error != PVR2D_OK) { |
| GST_ERROR_OBJECT (pvrvideosink, "Failed to blit. Error : %s", |
| gst_pvr2d_error_get_string (pvr_error)); |
| goto done; |
| } |
| dcontext->wsegl_table->pfnWSEGL_SwapDrawable (dcontext->drawable_handle, 1); |
| |
| if (draw_border) { |
| gst_pvrvideosink_xwindow_draw_borders (pvrvideosink, pvrvideosink->xwindow, |
| result); |
| pvrvideosink->redraw_borders = FALSE; |
| } |
| g_mutex_unlock (pvrvideosink->dcontext->x_lock); |
| |
| done: |
| GST_DEBUG_OBJECT (pvrvideosink, "end"); |
| g_mutex_unlock (pvrvideosink->flow_lock); |
| return; |
| |
| /* Error cases */ |
| |
| no_pvr_meta: |
| { |
| g_mutex_unlock (pvrvideosink->flow_lock); |
| GST_ERROR_OBJECT (pvrvideosink, "Got a buffer without GstPVRMeta"); |
| return; |
| } |
| } |
| |
| static void |
| gst_pvrvideosink_destroy_drawable (GstPVRVideoSink * pvrvideosink) |
| { |
| GST_DEBUG_OBJECT (pvrvideosink, "dcontext : %p", pvrvideosink->dcontext); |
| |
| if (pvrvideosink->dcontext != NULL) { |
| if (pvrvideosink->dcontext->drawable_handle) { |
| GST_DEBUG_OBJECT (pvrvideosink, "Deleting Drawable (drawable_handle:%p)", |
| pvrvideosink->dcontext->drawable_handle); |
| pvrvideosink->dcontext->wsegl_table-> |
| pfnWSEGL_DeleteDrawable (pvrvideosink->dcontext->drawable_handle); |
| } |
| |
| GST_DEBUG_OBJECT (pvrvideosink, "Closing display (display_handle:%p)", |
| pvrvideosink->dcontext->display_handle); |
| pvrvideosink->dcontext->wsegl_table-> |
| pfnWSEGL_CloseDisplay (pvrvideosink->dcontext->display_handle); |
| } |
| } |
| |
| /* We are called with the x_lock taken */ |
| static void |
| gst_pvrvideosink_pvrfill_rectangle (GstPVRVideoSink * pvrvideosink, |
| GstVideoRectangle rect) |
| { |
| PVR2DERROR pvr_error; |
| PPVR2DBLTINFO p_blt2d_info = 0; |
| GstDrawContext *dcontext = pvrvideosink->dcontext; |
| |
| GST_DEBUG_OBJECT (pvrvideosink, "begin"); |
| |
| p_blt2d_info = dcontext->p_blt2d_info; |
| |
| p_blt2d_info->pDstMemInfo = &dcontext->dst_mem; |
| p_blt2d_info->BlitFlags = PVR2D_BLIT_DISABLE_ALL; |
| p_blt2d_info->DstOffset = 0; |
| p_blt2d_info->CopyCode = PVR2DROPclear; |
| p_blt2d_info->DstStride = 4 * pvrvideosink->render_params.ui32Stride; |
| p_blt2d_info->DstFormat = PVR2D_ARGB8888; |
| p_blt2d_info->DstSurfWidth = pvrvideosink->xwindow->width; |
| p_blt2d_info->DstSurfHeight = pvrvideosink->xwindow->height; |
| p_blt2d_info->DstX = rect.x; |
| p_blt2d_info->DstY = rect.y; |
| p_blt2d_info->DSizeX = rect.w; |
| p_blt2d_info->DSizeY = rect.h; |
| |
| pvr_error = PVR2DBlt (pvrvideosink->dcontext->pvr_context, p_blt2d_info); |
| |
| if (pvr_error != PVR2D_OK) { |
| GST_ERROR_OBJECT (pvrvideosink, "Failed to blit. Error : %s", |
| gst_pvr2d_error_get_string (pvr_error)); |
| goto done; |
| } |
| dcontext->wsegl_table->pfnWSEGL_SwapDrawable (dcontext->drawable_handle, 1); |
| |
| done: |
| GST_DEBUG_OBJECT (pvrvideosink, "end"); |
| } |
| |
| /* We are called with the x_lock taken */ |
| static void |
| gst_pvrvideosink_xwindow_draw_borders (GstPVRVideoSink * pvrvideosink, |
| GstXWindow * xwindow, GstVideoRectangle rect) |
| { |
| gint t1, t2; |
| GstVideoRectangle result; |
| |
| g_return_if_fail (GST_IS_PVRVIDEOSINK (pvrvideosink)); |
| g_return_if_fail (xwindow != NULL); |
| |
| /* Left border */ |
| result.x = pvrvideosink->render_rect.x; |
| result.y = pvrvideosink->render_rect.y; |
| result.w = rect.x - pvrvideosink->render_rect.x; |
| result.h = pvrvideosink->render_rect.h; |
| if (rect.x > pvrvideosink->render_rect.x) |
| gst_pvrvideosink_pvrfill_rectangle (pvrvideosink, result); |
| |
| /* Right border */ |
| t1 = rect.x + rect.w; |
| t2 = pvrvideosink->render_rect.x + pvrvideosink->render_rect.w; |
| result.x = t1; |
| result.y = pvrvideosink->render_rect.y; |
| result.w = t2 - t1; |
| result.h = pvrvideosink->render_rect.h; |
| if (t1 < t2) |
| gst_pvrvideosink_pvrfill_rectangle (pvrvideosink, result); |
| |
| /* Top border */ |
| result.x = pvrvideosink->render_rect.x; |
| result.y = pvrvideosink->render_rect.y; |
| result.w = pvrvideosink->render_rect.w; |
| result.h = rect.y - pvrvideosink->render_rect.y; |
| if (rect.y > pvrvideosink->render_rect.y) |
| gst_pvrvideosink_pvrfill_rectangle (pvrvideosink, result); |
| |
| /* Bottom border */ |
| t1 = rect.y + rect.h; |
| t2 = pvrvideosink->render_rect.y + pvrvideosink->render_rect.h; |
| result.x = pvrvideosink->render_rect.x; |
| result.y = t1; |
| result.w = pvrvideosink->render_rect.w; |
| result.h = t2 - t1; |
| if (t1 < t2) |
| gst_pvrvideosink_pvrfill_rectangle (pvrvideosink, result); |
| } |
| |
| /* Element stuff */ |
| |
| static gboolean |
| gst_pvrvideosink_setcaps (GstBaseSink * bsink, GstCaps * caps) |
| { |
| GstPVRVideoSink *pvrvideosink; |
| GstVideoInfo info; |
| GstStructure *structure; |
| GstBufferPool *oldpool, *newpool; |
| |
| pvrvideosink = GST_PVRVIDEOSINK (bsink); |
| |
| GST_DEBUG_OBJECT (pvrvideosink, |
| "sinkconnect possible caps with given caps %" GST_PTR_FORMAT, caps); |
| |
| if (!gst_video_info_from_caps (&info, caps)) |
| goto invalid_format; |
| |
| GST_VIDEO_SINK_WIDTH (pvrvideosink) = info.width; |
| GST_VIDEO_SINK_HEIGHT (pvrvideosink) = info.height; |
| |
| /* Notify application to set xwindow id now */ |
| g_mutex_lock (pvrvideosink->flow_lock); |
| if (!pvrvideosink->xwindow) { |
| g_mutex_unlock (pvrvideosink->flow_lock); |
| gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (pvrvideosink)); |
| } else { |
| g_mutex_unlock (pvrvideosink->flow_lock); |
| } |
| |
| g_mutex_lock (pvrvideosink->flow_lock); |
| if (!pvrvideosink->xwindow) |
| pvrvideosink->xwindow = gst_pvrvideosink_create_window (pvrvideosink, |
| GST_VIDEO_SINK_WIDTH (pvrvideosink), |
| GST_VIDEO_SINK_HEIGHT (pvrvideosink)); |
| g_mutex_unlock (pvrvideosink->flow_lock); |
| |
| pvrvideosink->info = info; |
| |
| /* After a resize, we want to redraw the borders in case the new frame size |
| * doesn't cover the same area */ |
| pvrvideosink->redraw_borders = TRUE; |
| |
| /* create a new pool for the new configuration */ |
| newpool = gst_pvr_buffer_pool_new (GST_ELEMENT_CAST (pvrvideosink)); |
| |
| /* PVR needs at least 3 buffers */ |
| structure = gst_buffer_pool_get_config (newpool); |
| gst_buffer_pool_config_set (structure, caps, GST_VIDEO_INFO_SIZE (&info), 3, |
| 0, 0, 15); |
| if (!gst_buffer_pool_set_config (newpool, structure)) |
| goto config_failed; |
| |
| oldpool = pvrvideosink->pool; |
| pvrvideosink->pool = newpool; |
| g_mutex_unlock (pvrvideosink->flow_lock); |
| |
| /* unref the old sink */ |
| if (oldpool) { |
| /* we don't deactivate, some elements might still be using it, it will |
| * be deactivated when the last ref is gone */ |
| gst_object_unref (oldpool); |
| } |
| |
| return TRUE; |
| |
| config_failed: |
| { |
| GST_ERROR_OBJECT (pvrvideosink, "failed to set config."); |
| g_mutex_unlock (pvrvideosink->flow_lock); |
| return FALSE; |
| } |
| |
| invalid_format: |
| { |
| GST_DEBUG_OBJECT (pvrvideosink, |
| "Could not locate image format from caps %" GST_PTR_FORMAT, caps); |
| return FALSE; |
| } |
| } |
| |
| static GstCaps * |
| gst_pvrvideosink_getcaps (GstBaseSink * bsink, GstCaps * filter) |
| { |
| GstPVRVideoSink *pvrvideosink; |
| GstCaps *caps; |
| |
| GST_DEBUG_OBJECT (bsink, "filter:%" GST_PTR_FORMAT, filter); |
| |
| pvrvideosink = GST_PVRVIDEOSINK (bsink); |
| |
| /* FIXME : If we have curently configured caps, we should return those |
| * intersected with the filter*/ |
| |
| caps = gst_pad_get_pad_template_caps (GST_BASE_SINK (pvrvideosink)->sinkpad); |
| if (filter) { |
| GstCaps *intersection; |
| |
| intersection = |
| gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST); |
| gst_caps_unref (caps); |
| caps = intersection; |
| } |
| |
| GST_DEBUG_OBJECT (bsink, "Returning %" GST_PTR_FORMAT, caps); |
| |
| return caps; |
| } |
| |
| static GstStateChangeReturn |
| gst_pvrvideosink_change_state (GstElement * element, GstStateChange transition) |
| { |
| GstPVRVideoSink *pvrvideosink; |
| GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; |
| GstDrawContext *dcontext; |
| |
| pvrvideosink = GST_PVRVIDEOSINK (element); |
| |
| switch (transition) { |
| case GST_STATE_CHANGE_NULL_TO_READY: |
| if (pvrvideosink->dcontext == NULL) { |
| dcontext = gst_pvrvideosink_get_dcontext (pvrvideosink); |
| if (dcontext == NULL) |
| return GST_STATE_CHANGE_FAILURE; |
| GST_OBJECT_LOCK (pvrvideosink); |
| pvrvideosink->dcontext = dcontext; |
| GST_OBJECT_UNLOCK (pvrvideosink); |
| } |
| gst_pvrvideosink_manage_event_thread (pvrvideosink); |
| break; |
| case GST_STATE_CHANGE_READY_TO_PAUSED: |
| break; |
| case GST_STATE_CHANGE_PAUSED_TO_READY: |
| break; |
| case GST_STATE_CHANGE_PAUSED_TO_PLAYING: |
| break; |
| default: |
| break; |
| } |
| |
| ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); |
| |
| switch (transition) { |
| case GST_STATE_CHANGE_PLAYING_TO_PAUSED: |
| break; |
| case GST_STATE_CHANGE_PAUSED_TO_READY: |
| GST_VIDEO_SINK_WIDTH (pvrvideosink) = 0; |
| GST_VIDEO_SINK_HEIGHT (pvrvideosink) = 0; |
| break; |
| case GST_STATE_CHANGE_READY_TO_NULL: |
| gst_pvrvideosink_reset (pvrvideosink); |
| break; |
| default: |
| break; |
| } |
| |
| return ret; |
| } |
| |
| static void |
| gst_pvrvideosink_get_times (GstBaseSink * bsink, GstBuffer * buf, |
| GstClockTime * start, GstClockTime * end) |
| { |
| GstPVRVideoSink *pvrvideosink; |
| |
| pvrvideosink = GST_PVRVIDEOSINK (bsink); |
| |
| if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) { |
| *start = GST_BUFFER_TIMESTAMP (buf); |
| if (GST_BUFFER_DURATION_IS_VALID (buf)) { |
| *end = *start + GST_BUFFER_DURATION (buf); |
| } else { |
| gint fps_n, fps_d; |
| fps_n = GST_VIDEO_INFO_FPS_N (&pvrvideosink->info); |
| fps_d = GST_VIDEO_INFO_FPS_D (&pvrvideosink->info); |
| if (fps_n > 0) { |
| *end = *start + gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n); |
| } |
| } |
| } |
| } |
| |
| static GstFlowReturn |
| gst_pvrvideosink_show_frame (GstVideoSink * vsink, GstBuffer * buf) |
| { |
| GstPVRVideoSink *pvrvideosink = GST_PVRVIDEOSINK (vsink); |
| GstPVRMeta *meta; |
| |
| GST_DEBUG_OBJECT (pvrvideosink, "render buffer: %p", buf); |
| |
| meta = gst_buffer_get_pvr_meta (buf); |
| |
| if (G_UNLIKELY (meta == NULL)) { |
| meta = gst_buffer_add_pvr_meta (buf, GST_ELEMENT_CAST (pvrvideosink)); |
| if (meta == NULL) |
| goto meta_failure; |
| } |
| |
| gst_pvrvideosink_blit (pvrvideosink, buf); |
| |
| return GST_FLOW_OK; |
| |
| meta_failure: |
| { |
| GST_WARNING_OBJECT (pvrvideosink, "Failed to map incoming buffer"); |
| return GST_FLOW_ERROR; |
| } |
| } |
| |
| static gboolean |
| gst_pvrvideosink_propose_allocation (GstBaseSink * bsink, GstQuery * query) |
| { |
| GstPVRVideoSink *pvrvideosink = (GstPVRVideoSink *) bsink; |
| GstBufferPool *pool; |
| GstStructure *config; |
| GstCaps *caps; |
| guint size; |
| gboolean need_pool; |
| |
| gst_query_parse_allocation (query, &caps, &need_pool); |
| |
| if (caps == NULL) |
| goto no_caps; |
| |
| g_mutex_lock (pvrvideosink->flow_lock); |
| if ((pool = pvrvideosink->pool)) |
| gst_object_ref (pool); |
| g_mutex_unlock (pvrvideosink->flow_lock); |
| |
| if (pool != NULL) { |
| const GstCaps *pcaps; |
| |
| /* we had a pool, check caps */ |
| GST_DEBUG_OBJECT (pvrvideosink, "check existing pool caps"); |
| config = gst_buffer_pool_get_config (pool); |
| gst_buffer_pool_config_get (config, &pcaps, &size, NULL, NULL, NULL, NULL); |
| gst_structure_free (config); |
| |
| if (!gst_caps_is_equal (caps, pcaps)) { |
| GST_DEBUG_OBJECT (pvrvideosink, "pool has different caps"); |
| /* different caps, we can't use this pool */ |
| gst_object_unref (pool); |
| pool = NULL; |
| } |
| } |
| |
| if (pool == NULL && need_pool) { |
| GstVideoInfo info; |
| |
| GST_DEBUG_OBJECT (pvrvideosink, "create new pool"); |
| pool = gst_pvr_buffer_pool_new (GST_ELEMENT_CAST (pvrvideosink)); |
| |
| if (!gst_video_info_from_caps (&info, caps)) |
| goto invalid_caps; |
| |
| /* the normal size of a frame */ |
| size = info.size; |
| |
| config = gst_buffer_pool_get_config (pool); |
| gst_buffer_pool_config_set (config, caps, size, 0, 0, 0, 0); |
| if (!gst_buffer_pool_set_config (pool, config)) |
| goto config_failed; |
| } |
| /* we need at least 3 buffers */ |
| gst_query_set_allocation_params (query, size, 3, 0, 0, 0, pool); |
| |
| /* we also support various metadata */ |
| gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API, NULL); |
| |
| gst_object_unref (pool); |
| |
| return TRUE; |
| |
| /* ERRORS */ |
| no_caps: |
| { |
| GST_DEBUG_OBJECT (bsink, "no caps specified"); |
| return FALSE; |
| } |
| invalid_caps: |
| { |
| GST_DEBUG_OBJECT (bsink, "invalid caps specified"); |
| return FALSE; |
| } |
| config_failed: |
| { |
| GST_DEBUG_OBJECT (bsink, "failed setting config"); |
| return FALSE; |
| } |
| } |
| |
| /* Interfaces stuff */ |
| |
| /* This function destroys a GstXWindow */ |
| static void |
| gst_pvrvideosink_xwindow_destroy (GstPVRVideoSink * pvrvideosink, |
| GstXWindow * xwindow) |
| { |
| g_return_if_fail (xwindow != NULL); |
| |
| g_mutex_lock (pvrvideosink->dcontext->x_lock); |
| |
| /* If we did not create that window we just free the GC and let it live */ |
| if (xwindow->internal) |
| XDestroyWindow (pvrvideosink->dcontext->x_display, xwindow->window); |
| else |
| XSelectInput (pvrvideosink->dcontext->x_display, xwindow->window, 0); |
| |
| XFreeGC (pvrvideosink->dcontext->x_display, xwindow->gc); |
| |
| XSync (pvrvideosink->dcontext->x_display, FALSE); |
| |
| g_mutex_unlock (pvrvideosink->dcontext->x_lock); |
| |
| g_free (xwindow); |
| } |
| |
| static void |
| gst_pvrvideosink_set_window_handle (GstVideoOverlay * overlay, guintptr id) |
| { |
| XID window_handle = id; |
| GstPVRVideoSink *pvrvideosink = GST_PVRVIDEOSINK (overlay); |
| GstXWindow *xwindow = NULL; |
| |
| g_return_if_fail (GST_IS_PVRVIDEOSINK (pvrvideosink)); |
| |
| g_mutex_lock (pvrvideosink->flow_lock); |
| |
| /* If we already use that window return */ |
| if (pvrvideosink->xwindow && (window_handle == pvrvideosink->xwindow->window)) { |
| g_mutex_unlock (pvrvideosink->flow_lock); |
| return; |
| } |
| |
| /* If the element has not initialized the X11 context try to do so */ |
| if (!pvrvideosink->dcontext && !(pvrvideosink->dcontext = |
| gst_pvrvideosink_get_dcontext (pvrvideosink))) { |
| g_mutex_unlock (pvrvideosink->flow_lock); |
| /* we have thrown a GST_ELEMENT_ERROR now */ |
| return; |
| } |
| |
| /* If a window is there already we destroy it */ |
| if (pvrvideosink->xwindow) { |
| gst_pvrvideosink_xwindow_destroy (pvrvideosink, pvrvideosink->xwindow); |
| pvrvideosink->xwindow = NULL; |
| } |
| |
| /* If the xid is 0 we will create an internal one in buffer_alloc */ |
| if (window_handle != 0) { |
| XWindowAttributes attr; |
| WSEGLError glerror; |
| WSEGLDrawableParams source_params; |
| PVRSRV_CLIENT_MEM_INFO *client_mem_info; |
| |
| xwindow = g_new0 (GstXWindow, 1); |
| xwindow->window = window_handle; |
| |
| /* Set the event we want to receive and create a GC */ |
| g_mutex_lock (pvrvideosink->dcontext->x_lock); |
| |
| XGetWindowAttributes (pvrvideosink->dcontext->x_display, xwindow->window, |
| &attr); |
| |
| xwindow->width = attr.width; |
| xwindow->height = attr.height; |
| xwindow->internal = FALSE; |
| if (!pvrvideosink->have_render_rect) { |
| pvrvideosink->render_rect.x = pvrvideosink->render_rect.y = 0; |
| pvrvideosink->render_rect.w = attr.width; |
| pvrvideosink->render_rect.h = attr.height; |
| } |
| XSelectInput (pvrvideosink->dcontext->x_display, xwindow->window, |
| ExposureMask | StructureNotifyMask); |
| |
| XSetWindowBackgroundPixmap (pvrvideosink->dcontext->x_display, |
| xwindow->window, None); |
| |
| XMapWindow (pvrvideosink->dcontext->x_display, xwindow->window); |
| xwindow->gc = XCreateGC (pvrvideosink->dcontext->x_display, |
| xwindow->window, 0, NULL); |
| g_mutex_unlock (pvrvideosink->dcontext->x_lock); |
| |
| glerror = |
| pvrvideosink->dcontext->wsegl_table-> |
| pfnWSEGL_CreateWindowDrawable (pvrvideosink->dcontext->display_handle, |
| pvrvideosink->dcontext->glconfig, |
| &(pvrvideosink->dcontext->drawable_handle), |
| (NativeWindowType) xwindow->window, |
| &(pvrvideosink->dcontext->rotation)); |
| |
| if (glerror != WSEGL_SUCCESS) { |
| GST_ERROR_OBJECT (pvrvideosink, "Error creating drawable"); |
| return; |
| } |
| glerror = |
| pvrvideosink->dcontext->wsegl_table-> |
| pfnWSEGL_GetDrawableParameters (pvrvideosink->dcontext->drawable_handle, |
| &source_params, &pvrvideosink->render_params); |
| |
| client_mem_info = |
| (PVRSRV_CLIENT_MEM_INFO *) pvrvideosink->render_params.hPrivateData; |
| PVR2DMEMINFO_INITIALISE (&pvrvideosink->dcontext->dst_mem, client_mem_info); |
| } |
| |
| if (xwindow) |
| pvrvideosink->xwindow = xwindow; |
| |
| g_mutex_unlock (pvrvideosink->flow_lock); |
| } |
| |
| static void |
| gst_pvrvideosink_expose (GstVideoOverlay * overlay) |
| { |
| GstPVRVideoSink *pvrvideosink = GST_PVRVIDEOSINK (overlay); |
| |
| gst_pvrvideosink_blit (pvrvideosink, NULL); |
| } |
| |
| static void |
| gst_pvrvideosink_set_event_handling (GstVideoOverlay * overlay, |
| gboolean handle_events) |
| { |
| GstPVRVideoSink *pvrvideosink = GST_PVRVIDEOSINK (overlay); |
| |
| g_mutex_lock (pvrvideosink->flow_lock); |
| |
| if (G_UNLIKELY (!pvrvideosink->xwindow)) { |
| g_mutex_unlock (pvrvideosink->flow_lock); |
| return; |
| } |
| |
| g_mutex_lock (pvrvideosink->dcontext->x_lock); |
| |
| XSelectInput (pvrvideosink->dcontext->x_display, |
| pvrvideosink->xwindow->window, ExposureMask | StructureNotifyMask); |
| |
| g_mutex_unlock (pvrvideosink->dcontext->x_lock); |
| |
| g_mutex_unlock (pvrvideosink->flow_lock); |
| } |
| |
| static void |
| gst_pvrvideosink_set_render_rectangle (GstVideoOverlay * overlay, gint x, |
| gint y, gint width, gint height) |
| { |
| GstPVRVideoSink *pvrvideosink = GST_PVRVIDEOSINK (overlay); |
| |
| /* FIXME: how about some locking? */ |
| if (width >= 0 && height >= 0) { |
| pvrvideosink->render_rect.x = x; |
| pvrvideosink->render_rect.y = y; |
| pvrvideosink->render_rect.w = width; |
| pvrvideosink->render_rect.h = height; |
| pvrvideosink->have_render_rect = TRUE; |
| } else { |
| pvrvideosink->render_rect.x = 0; |
| pvrvideosink->render_rect.y = 0; |
| pvrvideosink->render_rect.w = pvrvideosink->xwindow->width; |
| pvrvideosink->render_rect.h = pvrvideosink->xwindow->height; |
| pvrvideosink->have_render_rect = FALSE; |
| } |
| } |
| |
| static void |
| gst_pvrvideosink_videooverlay_init (GstVideoOverlayInterface * iface) |
| { |
| iface->set_window_handle = gst_pvrvideosink_set_window_handle; |
| iface->expose = gst_pvrvideosink_expose; |
| iface->handle_events = gst_pvrvideosink_set_event_handling; |
| iface->set_render_rectangle = gst_pvrvideosink_set_render_rectangle; |
| } |
| |
| /* =========================================== */ |
| /* */ |
| /* Init & Class init */ |
| /* */ |
| /* =========================================== */ |
| |
| static void |
| gst_pvrvideosink_set_property (GObject * object, guint prop_id, |
| const GValue * value, GParamSpec * pspec) |
| { |
| GstPVRVideoSink *pvrvideosink; |
| |
| g_return_if_fail (GST_IS_PVRVIDEOSINK (object)); |
| |
| pvrvideosink = GST_PVRVIDEOSINK (object); |
| |
| switch (prop_id) { |
| case PROP_FORCE_ASPECT_RATIO: |
| pvrvideosink->keep_aspect = g_value_get_boolean (value); |
| break; |
| default: |
| G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
| break; |
| } |
| } |
| |
| static void |
| gst_pvrvideosink_get_property (GObject * object, guint prop_id, |
| GValue * value, GParamSpec * pspec) |
| { |
| GstPVRVideoSink *pvrvideosink; |
| |
| g_return_if_fail (GST_IS_PVRVIDEOSINK (object)); |
| |
| pvrvideosink = GST_PVRVIDEOSINK (object); |
| |
| switch (prop_id) { |
| case PROP_FORCE_ASPECT_RATIO: |
| g_value_set_boolean (value, pvrvideosink->keep_aspect); |
| break; |
| default: |
| G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
| break; |
| } |
| } |
| |
| void |
| gst_pvrvideosink_track_buffer (GstPVRVideoSink * pvrsink, GstBuffer * buffer) |
| { |
| GST_DEBUG_OBJECT (pvrsink, "Adding buffer %p to tracked buffers", buffer); |
| pvrsink->metabuffers = g_list_prepend (pvrsink->metabuffers, buffer); |
| } |
| |
| void |
| gst_pvrvideosink_untrack_buffer (GstPVRVideoSink * pvrsink, GstBuffer * buffer) |
| { |
| GST_DEBUG_OBJECT (pvrsink, "Removing buffer %p from tracked buffers", buffer); |
| pvrsink->metabuffers = g_list_remove_all (pvrsink->metabuffers, buffer); |
| } |
| |
| static void |
| gst_pvrvideosink_release_pvr_metas (GstPVRVideoSink * pvrsink) |
| { |
| GstBuffer *buf; |
| GstPVRMeta *pvrmeta; |
| |
| GST_DEBUG_OBJECT (pvrsink, "Releasing pending PVR metas"); |
| |
| while (pvrsink->metabuffers) { |
| buf = (GstBuffer *) pvrsink->metabuffers->data; |
| |
| pvrmeta = gst_buffer_get_pvr_meta (buf); |
| if (pvrmeta) |
| gst_buffer_remove_meta (buf, (GstMeta *) pvrmeta); |
| } |
| |
| GST_DEBUG_OBJECT (pvrsink, "Done"); |
| } |
| |
| static void |
| gst_pvrvideosink_dcontext_free (GstDrawContext * dcontext) |
| { |
| GST_DEBUG ("Freeing dcontext %p", dcontext); |
| |
| g_free (dcontext->p_blt_info); |
| g_free (dcontext->p_blt2d_info); |
| |
| if (dcontext->x_lock) |
| g_mutex_lock (dcontext->x_lock); |
| if (dcontext->x_display) { |
| GST_LOG ("Closing display"); |
| XCloseDisplay (dcontext->x_display); |
| } |
| if (dcontext->x_lock) { |
| g_mutex_unlock (dcontext->x_lock); |
| g_mutex_free (dcontext->x_lock); |
| } |
| |
| g_free (dcontext); |
| } |
| |
| static void |
| gst_pvrvideosink_dcontext_clear (GstPVRVideoSink * pvrvideosink) |
| { |
| GstDrawContext *dcontext; |
| |
| GST_DEBUG_OBJECT (pvrvideosink, "Clearing dcontext %p", |
| pvrvideosink->dcontext); |
| |
| GST_OBJECT_LOCK (pvrvideosink); |
| if (!pvrvideosink->dcontext) { |
| GST_OBJECT_UNLOCK (pvrvideosink); |
| return; |
| } |
| |
| dcontext = pvrvideosink->dcontext; |
| pvrvideosink->dcontext = NULL; |
| GST_OBJECT_UNLOCK (pvrvideosink); |
| |
| gst_pvrvideosink_dcontext_free (dcontext); |
| } |
| |
| static void |
| gst_pvrvideosink_reset (GstPVRVideoSink * pvrvideosink) |
| { |
| GThread *thread; |
| |
| GST_DEBUG_OBJECT (pvrvideosink, "Resetting"); |
| |
| GST_OBJECT_LOCK (pvrvideosink); |
| pvrvideosink->running = FALSE; |
| thread = pvrvideosink->event_thread; |
| pvrvideosink->event_thread = NULL; |
| GST_OBJECT_UNLOCK (pvrvideosink); |
| |
| if (thread) |
| g_thread_join (thread); |
| |
| if (pvrvideosink->current_buffer) { |
| GST_LOG_OBJECT (pvrvideosink, "Removing cached buffer"); |
| gst_buffer_unref (pvrvideosink->current_buffer); |
| pvrvideosink->current_buffer = NULL; |
| } |
| |
| if (pvrvideosink->pool) { |
| GST_LOG_OBJECT (pvrvideosink, "Unreffing pool"); |
| gst_object_unref (pvrvideosink->pool); |
| pvrvideosink->pool = NULL; |
| } |
| memset (&pvrvideosink->render_params, 0, sizeof (WSEGLDrawableParams)); |
| |
| pvrvideosink->render_rect.x = pvrvideosink->render_rect.y = 0; |
| pvrvideosink->render_rect.w = pvrvideosink->render_rect.h = 0; |
| pvrvideosink->have_render_rect = FALSE; |
| |
| gst_pvrvideosink_release_pvr_metas (pvrvideosink); |
| |
| gst_pvrvideosink_destroy_drawable (pvrvideosink); |
| |
| if (pvrvideosink->xwindow) { |
| gst_pvrvideosink_xwindow_destroy (pvrvideosink, pvrvideosink->xwindow); |
| pvrvideosink->xwindow = NULL; |
| } |
| |
| gst_pvrvideosink_dcontext_clear (pvrvideosink); |
| } |
| |
| static void |
| gst_pvrvideosink_finalize (GObject * object) |
| { |
| GstPVRVideoSink *pvrvideosink; |
| |
| pvrvideosink = GST_PVRVIDEOSINK (object); |
| |
| gst_pvrvideosink_reset (pvrvideosink); |
| |
| if (pvrvideosink->flow_lock) { |
| g_mutex_free (pvrvideosink->flow_lock); |
| pvrvideosink->flow_lock = NULL; |
| } |
| |
| G_OBJECT_CLASS (parent_class)->finalize (object); |
| } |
| |
| static void |
| gst_pvrvideosink_init (GstPVRVideoSink * pvrvideosink) |
| { |
| pvrvideosink->running = FALSE; |
| |
| pvrvideosink->flow_lock = g_mutex_new (); |
| pvrvideosink->pool = NULL; |
| |
| pvrvideosink->keep_aspect = FALSE; |
| pvrvideosink->current_caps = NULL; |
| pvrvideosink->dcontext = NULL; |
| pvrvideosink->xwindow = NULL; |
| pvrvideosink->redraw_borders = TRUE; |
| pvrvideosink->current_buffer = NULL; |
| pvrvideosink->event_thread = NULL; |
| memset (&pvrvideosink->render_params, 0, sizeof (WSEGLDrawableParams)); |
| } |
| |
| static void |
| gst_pvrvideosink_class_init (GstPVRVideoSinkClass * klass) |
| { |
| GObjectClass *gobject_class; |
| GstElementClass *gstelement_class; |
| GstBaseSinkClass *gstbasesink_class; |
| GstVideoSinkClass *videosink_class; |
| |
| gobject_class = (GObjectClass *) klass; |
| gstelement_class = (GstElementClass *) klass; |
| gstbasesink_class = (GstBaseSinkClass *) klass; |
| videosink_class = (GstVideoSinkClass *) klass; |
| |
| parent_class = g_type_class_peek_parent (klass); |
| |
| gobject_class->finalize = gst_pvrvideosink_finalize; |
| gobject_class->set_property = gst_pvrvideosink_set_property; |
| gobject_class->get_property = gst_pvrvideosink_get_property; |
| |
| g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO, |
| g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio", |
| "When enabled, reverse caps negotiation (scaling) will respect " |
| "original aspect ratio", TRUE, |
| G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
| |
| gst_element_class_set_static_metadata (gstelement_class, |
| "PVR Video sink", "Sink/Video", |
| "A PVR videosink", |
| "Luciana Fujii Pontello <luciana.fujii@collabora.co.uk"); |
| |
| gst_element_class_add_static_pad_template (gstelement_class, |
| &gst_pvrvideosink_sink_template_factory); |
| |
| gstelement_class->change_state = gst_pvrvideosink_change_state; |
| |
| gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_pvrvideosink_setcaps); |
| gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_pvrvideosink_getcaps); |
| gstbasesink_class->propose_allocation = |
| GST_DEBUG_FUNCPTR (gst_pvrvideosink_propose_allocation); |
| gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_pvrvideosink_get_times); |
| |
| videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_pvrvideosink_show_frame); |
| } |