diff options
author | nvidia <nvidia@nvidia.com> | 2015-11-04 15:57:23 +0000 |
---|---|---|
committer | Javier Jardón <jjardon@gnome.org> | 2015-11-04 16:07:49 +0000 |
commit | 72cd0878cf111dd1ec4081ece6d188122a293d64 (patch) | |
tree | 41c772015ff05a956e794fb3f419e00348e339bf /omx/gstomxvideosink.c | |
parent | f3f9330332050f6e18b8fbeabf3791604b862ab0 (diff) | |
download | gst-omx-72cd0878cf111dd1ec4081ece6d188122a293d64.tar.gz |
Add NVIDIA omx implementationbaserock/1.0.0.1/tegra
Diffstat (limited to 'omx/gstomxvideosink.c')
-rw-r--r-- | omx/gstomxvideosink.c | 1452 |
1 files changed, 1452 insertions, 0 deletions
diff --git a/omx/gstomxvideosink.c b/omx/gstomxvideosink.c new file mode 100644 index 0000000..54326f2 --- /dev/null +++ b/omx/gstomxvideosink.c @@ -0,0 +1,1452 @@ +/* GStreamer + * Copyright (c) 2013-2015, NVIDIA CORPORATION. All rights reserved. + * + * 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 <gst/gst.h> +#include <gst/video/gstvideosink.h> +#include <gst/video/gstvideometa.h> +#include <gst/video/gstvideopool.h> + +#include "gstomxvideosink.h" + +GST_DEBUG_CATEGORY_STATIC (gst_omx_video_sink_debug_category); +#define GST_CAT_DEFAULT gst_omx_video_sink_debug_category + +#define GST_OMX_SINK_MEMORY_TYPE "omxsink" + +/* OpenMax memory allocator Implementation */ + +typedef struct _GstOmxSinkMemory GstOmxSinkMemory; +typedef struct _GstOmxSinkMemoryAllocator GstOmxSinkMemoryAllocator; +typedef struct _GstOmxSinkMemoryAllocatorClass GstOmxSinkMemoryAllocatorClass; + +struct _GstOmxSinkMemory +{ + GstMemory mem; + + GstOMXBuffer *buf; +}; + +struct _GstOmxSinkMemoryAllocator +{ + GstAllocator parent; +}; + +struct _GstOmxSinkMemoryAllocatorClass +{ + GstAllocatorClass parent_class; +}; + +static GstMemory * +gst_omx_sink_memory_allocator_alloc_dummy (GstAllocator * allocator, gsize size, + GstAllocationParams * params) +{ + g_assert_not_reached (); + return NULL; +} + +static void +gst_omx_sink_memory_allocator_free (GstAllocator * allocator, GstMemory * mem) +{ + GstOmxSinkMemory *omem = (GstOmxSinkMemory *) mem; + g_slice_free (GstOmxSinkMemory, omem); +} + +static gpointer +gst_omx_sink_memory_map (GstMemory * mem, gsize maxsize, GstMapFlags flags) +{ + GstOmxSinkMemory *omem = (GstOmxSinkMemory *) mem; + return omem->buf->omx_buf->pBuffer + omem->mem.offset; +} + +static void +gst_omx_sink_memory_unmap (GstMemory * mem) +{ +} + +static GstMemory * +gst_omx_sink_memory_share (GstMemory * mem, gssize offset, gssize size) +{ + g_assert_not_reached (); + return NULL; +} + +GType gst_omx_sink_memory_allocator_get_type (void); +G_DEFINE_TYPE (GstOmxSinkMemoryAllocator, gst_omx_sink_memory_allocator, + GST_TYPE_ALLOCATOR); + +#define GST_TYPE_OMX_SINK_MEMORY_ALLOCATOR (gst_omx_sink_memory_allocator_get_type()) +#define GST_IS_OMX_SINK_MEMORY_ALLOCATOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_OMX_SINK_MEMORY_ALLOCATOR)) + +#define DEFAULT_OVERLAY 1 +#define DEFAULT_OVERLAY_DEPTH 0 +#define DEFAULT_OVERLAY_X 0 +#define DEFAULT_OVERLAY_Y 0 +#define DEFAULT_OVERLAY_W 0 +#define DEFAULT_OVERLAY_H 0 + +enum +{ + PROP_0, + PROP_OVERLAY, + PROP_OVERLAY_DEPTH, + PROP_OVERLAY_X, + PROP_OVERLAY_Y, + PROP_OVERLAY_W, + PROP_OVERLAY_H +}; + +static void +gst_omx_sink_memory_allocator_class_init (GstOmxSinkMemoryAllocatorClass * + klass) +{ + GstAllocatorClass *allocator_class; + + allocator_class = (GstAllocatorClass *) klass; + + allocator_class->alloc = gst_omx_sink_memory_allocator_alloc_dummy; + allocator_class->free = gst_omx_sink_memory_allocator_free; +} + +static void +gst_omx_sink_memory_allocator_init (GstOmxSinkMemoryAllocator * allocator) +{ + GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator); + + alloc->mem_type = GST_OMX_SINK_MEMORY_TYPE; + alloc->mem_map = gst_omx_sink_memory_map; + alloc->mem_unmap = gst_omx_sink_memory_unmap; + alloc->mem_share = gst_omx_sink_memory_share; + + GST_OBJECT_FLAG_SET (allocator, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC); +} + +static GstMemory * +gst_omx_sink_memory_allocator_alloc (GstAllocator * allocator, + GstMemoryFlags flags, GstOMXBuffer * buf) +{ + GstOmxSinkMemory *mem; + + mem = g_slice_new (GstOmxSinkMemory); + /* the shared memory is always readonly */ + gst_memory_init (GST_MEMORY_CAST (mem), flags, allocator, NULL, + buf->omx_buf->nAllocLen, buf->port->port_def.nBufferAlignment, + 0, buf->omx_buf->nAllocLen); + + mem->buf = buf; + + return GST_MEMORY_CAST (mem); +} + +/* Buffer Pool of openmax buffers */ + +GQuark gst_omx_sink_data_quark = 0; +typedef struct _GstOmxSinkBufferPool GstOmxSinkBufferPool; +typedef struct _GstOmxSinkBufferPoolClass GstOmxSinkBufferPoolClass; +#define GST_OMX_SINK_BUFFER_POOL(pool) ((GstOmxSinkBufferPool *) pool) + +struct _GstOmxSinkBufferPool +{ + GstBufferPool parent; + + GstElement *element; + + GstCaps *caps; + gboolean add_videometa; + GstVideoInfo video_info; + + GstOMXComponent *component; + GstOMXPort *port; + + GstAllocator *allocator; + + /* Used during alloc to specify + * which buffer has to be wrapped. + */ + guint current_buffer_index; +}; + +struct _GstOmxSinkBufferPoolClass +{ + GstBufferPoolClass parent_class; +}; + +GType gst_omx_sink_buffer_pool_get_type (void); + +G_DEFINE_TYPE (GstOmxSinkBufferPool, gst_omx_sink_buffer_pool, + GST_TYPE_BUFFER_POOL); + +#define GST_TYPE_OMX_SINK_BUFFER_POOL (gst_omx_sink_buffer_pool_get_type()) + + +static void +gst_omx_sink_buffer_pool_finalize (GObject * object) +{ + GstOmxSinkBufferPool *pool = GST_OMX_SINK_BUFFER_POOL (object); + + if (pool->element) + gst_object_unref (pool->element); + pool->element = NULL; + + if (pool->allocator) + gst_object_unref (pool->allocator); + pool->allocator = NULL; + + if (pool->caps) + gst_caps_unref (pool->caps); + pool->caps = NULL; + + G_OBJECT_CLASS (gst_omx_sink_buffer_pool_parent_class)->finalize (object); +} + +static gboolean +gst_omx_sink_buffer_pool_start (GstBufferPool * bpool) +{ + GstOmxSinkBufferPool *pool = GST_OMX_SINK_BUFFER_POOL (bpool); + GstOmxVideoSink *self = GST_OMX_VIDEO_SINK (pool->element); + GstCaps *caps; + guint min, max; + GstStructure *config; + GstOMXPort *port = pool->port; + OMX_ERRORTYPE err = OMX_ErrorNone; + + + /* Only allow to start the pool if we still are attached + * to a component and port */ + GST_OBJECT_LOCK (pool); + if (!pool->component || !pool->port) { + GST_OBJECT_UNLOCK (pool); + return FALSE; + } + + config = gst_buffer_pool_get_config (bpool); + gst_buffer_pool_config_get_params (config, &caps, NULL, &min, &max); + gst_structure_free (config); + + min = MAX (min, max); + + if (min > port->port_def.nBufferCountActual) { + err = gst_omx_port_update_port_definition (port, NULL); + if (err == OMX_ErrorNone) { + port->port_def.nBufferCountActual = min; + err = gst_omx_port_update_port_definition (port, &port->port_def); + } + } + if (!gst_omx_port_is_enabled (port)) { + err = gst_omx_port_set_enabled (port, TRUE); + if (err != OMX_ErrorNone) { + GST_INFO_OBJECT (self, + "Failed to enable port: %s (0x%08x)", + gst_omx_error_to_string (err), err); + GST_OBJECT_UNLOCK (pool); + return FALSE; + } + if (gst_omx_port_allocate_buffers (self->sink_in_port) != OMX_ErrorNone) { + GST_OBJECT_UNLOCK (pool); + return FALSE; + } + if (gst_omx_port_wait_enabled (self->sink_in_port, + 5 * GST_SECOND) != OMX_ErrorNone) { + GST_OBJECT_UNLOCK (pool); + return FALSE; + } + } + + GST_OBJECT_UNLOCK (pool); + + return + GST_BUFFER_POOL_CLASS (gst_omx_sink_buffer_pool_parent_class)->start + (bpool); +} + +static gboolean +gst_omx_sink_buffer_pool_stop (GstBufferPool * bpool) +{ + GstOmxSinkBufferPool *pool = GST_OMX_SINK_BUFFER_POOL (bpool); + + if (pool->caps) + gst_caps_unref (pool->caps); + pool->caps = NULL; + + pool->add_videometa = FALSE; + + return + GST_BUFFER_POOL_CLASS (gst_omx_sink_buffer_pool_parent_class)->stop + (bpool); +} + +static const gchar ** +gst_omx_sink_buffer_pool_get_options (GstBufferPool * bpool) +{ + static const gchar *raw_video_options[] = + { GST_BUFFER_POOL_OPTION_VIDEO_META, NULL }; + static const gchar *options[] = { NULL }; + GstOmxSinkBufferPool *pool = GST_OMX_SINK_BUFFER_POOL (bpool); + + GST_OBJECT_LOCK (pool); + if (pool->port && pool->port->port_def.eDomain == OMX_PortDomainVideo + && pool->port->port_def.format.video.eCompressionFormat == + OMX_VIDEO_CodingUnused) { + GST_OBJECT_UNLOCK (pool); + return raw_video_options; + } + GST_OBJECT_UNLOCK (pool); + + return options; +} + +static gboolean +gst_omx_sink_buffer_pool_set_config (GstBufferPool * bpool, + GstStructure * config) +{ + GstOmxSinkBufferPool *pool = GST_OMX_SINK_BUFFER_POOL (bpool); + GstCaps *caps; + + GST_OBJECT_LOCK (pool); + + if (!gst_buffer_pool_config_get_params (config, &caps, NULL, NULL, NULL)) + goto wrong_config; + + if (caps == NULL) + goto no_caps; + + if (pool->port && pool->port->port_def.eDomain == OMX_PortDomainVideo + && pool->port->port_def.format.video.eCompressionFormat == + OMX_VIDEO_CodingUnused) { + GstVideoInfo info; + + /* now parse the caps from the config */ + if (!gst_video_info_from_caps (&info, caps)) + goto wrong_video_caps; + + /* enable metadata based on config of the pool */ + pool->add_videometa = + gst_buffer_pool_config_has_option (config, + GST_BUFFER_POOL_OPTION_VIDEO_META); + + pool->video_info = info; + } + + if (pool->caps) + gst_caps_unref (pool->caps); + pool->caps = gst_caps_ref (caps); + + GST_OBJECT_UNLOCK (pool); + + return + GST_BUFFER_POOL_CLASS (gst_omx_sink_buffer_pool_parent_class)->set_config + (bpool, config); + + /* ERRORS */ +wrong_config: + { + GST_OBJECT_UNLOCK (pool); + GST_WARNING_OBJECT (pool, "invalid config"); + return FALSE; + } +no_caps: + { + GST_OBJECT_UNLOCK (pool); + GST_WARNING_OBJECT (pool, "no caps in config"); + return FALSE; + } +wrong_video_caps: + { + GST_OBJECT_UNLOCK (pool); + GST_WARNING_OBJECT (pool, + "failed getting geometry from caps %" GST_PTR_FORMAT, caps); + return FALSE; + } +} + +static GstFlowReturn +gst_omx_sink_buffer_pool_alloc_buffer (GstBufferPool * bpool, + GstBuffer ** buffer, GstBufferPoolAcquireParams * params) +{ + GstOmxSinkBufferPool *pool = GST_OMX_SINK_BUFFER_POOL (bpool); + GstBuffer *buf; + GstOMXBuffer *omx_buf; + GstMemory *mem; + + omx_buf = g_ptr_array_index (pool->port->buffers, pool->current_buffer_index); + g_return_val_if_fail (omx_buf != NULL, GST_FLOW_ERROR); + + mem = gst_omx_sink_memory_allocator_alloc (pool->allocator, 0, omx_buf); + buf = gst_buffer_new (); + gst_buffer_append_memory (buf, mem); + + if (pool->add_videometa) { + gsize offset[4] = { 0, }; + gint stride[4] = { 0, }; + + switch (pool->video_info.finfo->format) { + case GST_VIDEO_FORMAT_I420: + offset[0] = 0; + stride[0] = pool->port->port_def.format.video.nStride; + offset[1] = stride[0] * pool->port->port_def.format.video.nSliceHeight; + stride[1] = pool->port->port_def.format.video.nStride / 2; + offset[2] = + offset[1] + + stride[1] * (pool->port->port_def.format.video.nSliceHeight / 2); + stride[2] = pool->port->port_def.format.video.nStride / 2; + break; + case GST_VIDEO_FORMAT_NV12: + offset[0] = 0; + stride[0] = pool->port->port_def.format.video.nStride; + offset[1] = stride[0] * pool->port->port_def.format.video.nSliceHeight; + stride[1] = pool->port->port_def.format.video.nStride; + break; + default: + g_assert_not_reached (); + break; + } + + gst_buffer_add_video_meta_full (buf, GST_VIDEO_FRAME_FLAG_NONE, + GST_VIDEO_INFO_FORMAT (&pool->video_info), + GST_VIDEO_INFO_WIDTH (&pool->video_info), + GST_VIDEO_INFO_HEIGHT (&pool->video_info), + GST_VIDEO_INFO_N_PLANES (&pool->video_info), offset, stride); + } + + gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (buf), + gst_omx_sink_data_quark, omx_buf, NULL); + + *buffer = buf; + + pool->current_buffer_index++; + + return GST_FLOW_OK; +} + +static void +gst_omx_sink_buffer_pool_free_buffer (GstBufferPool * bpool, GstBuffer * buffer) +{ + GstOmxSinkBufferPool *pool = GST_OMX_SINK_BUFFER_POOL (bpool); + + gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (buffer), + gst_omx_sink_data_quark, NULL, NULL); + + GST_BUFFER_POOL_CLASS (gst_omx_sink_buffer_pool_parent_class)->free_buffer + (bpool, buffer); + + pool->current_buffer_index--; +} + +static void +gst_omx_sink_buffer_pool_class_init (GstOmxSinkBufferPoolClass * klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + GstBufferPoolClass *gstbufferpool_class = (GstBufferPoolClass *) klass; + + gst_omx_sink_data_quark = g_quark_from_static_string ("GstOmxSinkBufferData"); + + gobject_class->finalize = gst_omx_sink_buffer_pool_finalize; + gstbufferpool_class->start = gst_omx_sink_buffer_pool_start; + gstbufferpool_class->stop = gst_omx_sink_buffer_pool_stop; + gstbufferpool_class->get_options = gst_omx_sink_buffer_pool_get_options; + gstbufferpool_class->set_config = gst_omx_sink_buffer_pool_set_config; + gstbufferpool_class->alloc_buffer = gst_omx_sink_buffer_pool_alloc_buffer; + gstbufferpool_class->free_buffer = gst_omx_sink_buffer_pool_free_buffer; +} + +static void +gst_omx_sink_buffer_pool_init (GstOmxSinkBufferPool * pool) +{ + pool->allocator = + g_object_new (gst_omx_sink_memory_allocator_get_type (), NULL); + pool->current_buffer_index = 0; +} + +static GstBufferPool * +gst_omx_sink_buffer_pool_new (GstElement * element, GstOMXComponent * component, + GstOMXPort * port) +{ + GstOmxSinkBufferPool *pool; + + pool = g_object_new (GST_TYPE_OMX_SINK_BUFFER_POOL, NULL); + pool->element = gst_object_ref (element); + pool->component = component; + pool->port = port; + + return GST_BUFFER_POOL (pool); +} + +/* prototypes */ + +static GstFlowReturn +gst_omx_video_sink_show_frame (GstVideoSink * video_sink, GstBuffer * buf); + +static void +gst_omx_video_sink_check_nvfeatures (GstOmxVideoSink * self, GstCaps * caps); + +gboolean gst_omx_video_sink_start (GstBaseSink * sink); +gboolean gst_omx_video_sink_stop (GstBaseSink * videosink); +gboolean gst_omx_video_sink_setcaps (GstBaseSink * sink, GstCaps * caps); +GstCaps *gst_omx_video_sink_getcaps (GstBaseSink * sink, GstCaps * filter); +gboolean gst_omx_video_sink_propose_allocation (GstBaseSink * sink, + GstQuery * query); +void gst_omx_video_sink_get_times (GstBaseSink * sink, GstBuffer * buffer, + GstClockTime * start, GstClockTime * end); +static gboolean gst_omx_video_sink_shutdown (GstOmxVideoSink * self); +static gboolean +gst_omx_video_sink_event (GstBaseSink * sink, GstEvent * event); + +static void +gst_omx_video_sink_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void +gst_omx_video_sink_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +/* class initialization */ + +#define gst_omx_video_sink_parent_class parent_class + +G_DEFINE_TYPE_WITH_CODE (GstOmxVideoSink, gst_omx_video_sink, + GST_TYPE_VIDEO_SINK, + GST_DEBUG_CATEGORY_INIT (gst_omx_video_sink_debug_category, "omxvideosink", + 0, "debug category for omxvideosink element")); + + +static OMX_ERRORTYPE +Update_Overlay_PlaneBlend (GstOmxVideoSink * self) +{ + OMX_ERRORTYPE eError = OMX_ErrorNone; + OMX_CONFIG_PLANEBLENDTYPE odepth; + + GST_OMX_INIT_STRUCT (&odepth); + odepth.nPortIndex = self->sink_in_port->index; + + eError = + gst_omx_component_get_config (GST_OMX_VIDEO_SINK (self)->sink, + OMX_IndexConfigCommonPlaneBlend, + &odepth); + if (eError == OMX_ErrorNone) { + odepth.nDepth = self->overlay_depth; + eError = + gst_omx_component_set_config (GST_OMX_VIDEO_SINK (self)->sink, + OMX_IndexConfigCommonPlaneBlend, + &odepth); + } + + return eError; +} + + +static OMX_ERRORTYPE +Update_Overlay_Position (GstOmxVideoSink * self) +{ + OMX_ERRORTYPE eError = OMX_ErrorNone; + OMX_CONFIG_POINTTYPE position; + + if (self->update_pos) { + GST_OMX_INIT_STRUCT (&position); + position.nPortIndex = self->sink_in_port->index; + + eError = + gst_omx_component_get_config (GST_OMX_VIDEO_SINK (self)->sink, + OMX_IndexConfigCommonOutputPosition, + &position); + if (eError == OMX_ErrorNone) { + position.nX = self->overlay_x; + position.nY = self->overlay_y; + eError = + gst_omx_component_set_config (GST_OMX_VIDEO_SINK (self)->sink, + OMX_IndexConfigCommonOutputPosition, + &position); + self->update_pos = FALSE; + } + } + + return eError; +} + +static OMX_ERRORTYPE +Update_Overlay_Size (GstOmxVideoSink * self) +{ + OMX_ERRORTYPE eError = OMX_ErrorNone; + OMX_FRAMESIZETYPE osize; + + if (self->update_size) { + GST_OMX_INIT_STRUCT (&osize); + osize.nPortIndex = self->sink_in_port->index; + + eError = + gst_omx_component_get_config (GST_OMX_VIDEO_SINK (self)->sink, + OMX_IndexConfigCommonOutputSize, + &osize); + if (eError == OMX_ErrorNone) { + osize.nWidth = self->overlay_w; + osize.nHeight = self->overlay_h; + eError = + gst_omx_component_set_config (GST_OMX_VIDEO_SINK (self)->sink, + OMX_IndexConfigCommonOutputSize, + &osize); + self->update_size = FALSE; + } + } + + return eError; +} + +static GstStateChangeReturn +gst_omx_video_sink_change_state (GstElement * element, + GstStateChange transition) +{ + GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; + GstOmxVideoSink *omxsink; + + omxsink = GST_OMX_VIDEO_SINK (element); + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + omxsink->fps_n = 0; + omxsink->fps_d = 1; + GST_VIDEO_SINK_WIDTH (omxsink) = 0; + GST_VIDEO_SINK_HEIGHT (omxsink) = 0; + + g_mutex_lock (&omxsink->flow_lock); + if (omxsink->pool) + gst_buffer_pool_set_active (omxsink->pool, FALSE); + g_mutex_unlock (&omxsink->flow_lock); + + if (omxsink->sink_in_port) + gst_omx_port_set_flushing (omxsink->sink_in_port, 5 * GST_SECOND, TRUE); + 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: + /* + * When running a stream in loop, the pipeline is set in READY state. + * In this state we need to ensure that buffers on input port of + * sink are deallocated without the sink going in NULL state. + * reconfigure flag is set here which is checked in setcaps function + * to reconfigure sink's input port incase setcaps is called again. + */ + omxsink->sink_in_port->reconfigure = TRUE; + break; + case GST_STATE_CHANGE_READY_TO_NULL: + break; + default: + break; + } + + return ret; +} + +static void +gst_omx_video_sink_class_init (GstOmxVideoSinkClass * klass) +{ + GstElementClass *gstelement_class = (GstElementClass *) klass; + GstVideoSinkClass *videosink_class = GST_VIDEO_SINK_CLASS (klass); + GstBaseSinkClass *basesink_class = GST_BASE_SINK_CLASS (klass); + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gstelement_class->change_state = gst_omx_video_sink_change_state; + + gobject_class->set_property = gst_omx_video_sink_set_property; + gobject_class->get_property = gst_omx_video_sink_get_property; + + basesink_class->start = GST_DEBUG_FUNCPTR (gst_omx_video_sink_start); + basesink_class->stop = GST_DEBUG_FUNCPTR (gst_omx_video_sink_stop); + basesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_omx_video_sink_setcaps); + basesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_omx_video_sink_getcaps); + basesink_class->get_times = GST_DEBUG_FUNCPTR (gst_omx_video_sink_get_times); + basesink_class->propose_allocation = + GST_DEBUG_FUNCPTR (gst_omx_video_sink_propose_allocation); + basesink_class->event = GST_DEBUG_FUNCPTR (gst_omx_video_sink_event); + + videosink_class->show_frame = + GST_DEBUG_FUNCPTR (gst_omx_video_sink_show_frame); + + /* + * Overlay Index. + */ + g_object_class_install_property (gobject_class, PROP_OVERLAY, + g_param_spec_uint ("overlay", "overlay", + "Overlay index", 1, 2, + DEFAULT_OVERLAY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /* + * Overlay Depth. + */ + g_object_class_install_property (gobject_class, PROP_OVERLAY_DEPTH, + g_param_spec_uint ("overlay-depth", "overlay-depth", + "Overlay depth", 0, 2, + DEFAULT_OVERLAY_DEPTH, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /* + * Overlay X coordinate. + */ + g_object_class_install_property (gobject_class, PROP_OVERLAY_X, + g_param_spec_uint ("overlay-x", "overlay-x", + "Overlay X coordinate", 0, G_MAXUINT, + DEFAULT_OVERLAY_X, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /* + * Overlay Y coordinate. + */ + g_object_class_install_property (gobject_class, PROP_OVERLAY_Y, + g_param_spec_uint ("overlay-y", "overlay-y", + "Overlay Y coordinate", 0, G_MAXUINT, + DEFAULT_OVERLAY_Y, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /* + * Overlay Width. + */ + g_object_class_install_property (gobject_class, PROP_OVERLAY_W, + g_param_spec_uint ("overlay-w", "overlay-w", + "Overlay Width", 0, G_MAXUINT, + DEFAULT_OVERLAY_W, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /* + * Overlay Height. + */ + g_object_class_install_property (gobject_class, PROP_OVERLAY_H, + g_param_spec_uint ("overlay-h", "overlay-h", + "Overlay Height", 0, G_MAXUINT, + DEFAULT_OVERLAY_H, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); +} + +static void +gst_omx_video_sink_init (GstOmxVideoSink * omxvideosink) +{ + omxvideosink->fps_n = 0; + omxvideosink->fps_d = 1; + omxvideosink->cur_buf = NULL; + omxvideosink->hw_path = FALSE; + + omxvideosink->overlay = DEFAULT_OVERLAY; + omxvideosink->overlay_depth = DEFAULT_OVERLAY_DEPTH; + omxvideosink->overlay_x = DEFAULT_OVERLAY_X; + omxvideosink->overlay_y = DEFAULT_OVERLAY_Y; + omxvideosink->overlay_w = DEFAULT_OVERLAY_W; + omxvideosink->overlay_h = DEFAULT_OVERLAY_H; + + omxvideosink->update_pos = FALSE; + omxvideosink->update_size = FALSE; +} + +static void +gst_omx_video_sink_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstOmxVideoSink * self = GST_OMX_VIDEO_SINK (object); + + switch (prop_id) { + case PROP_OVERLAY: + self->overlay = g_value_get_uint (value); + break; + case PROP_OVERLAY_DEPTH: + self->overlay_depth = g_value_get_uint (value); + break; + case PROP_OVERLAY_X: + { + guint x = g_value_get_uint (value); + if (self->overlay_x != x) + self->update_pos = TRUE; + self->overlay_x = x; + } + break; + case PROP_OVERLAY_Y: + { + guint y = g_value_get_uint (value); + if (self->overlay_y != y) + self->update_pos = TRUE; + self->overlay_y = y; + } + break; + case PROP_OVERLAY_W: + { + guint w = g_value_get_uint (value); + if (self->overlay_w != w) + self->update_size = TRUE; + self->overlay_w = w; + } + break; + case PROP_OVERLAY_H: + { + guint h = g_value_get_uint (value); + if (self->overlay_h != h) + self->update_size = TRUE; + self->overlay_h = h; + } + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_omx_video_sink_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstOmxVideoSink * self = GST_OMX_VIDEO_SINK (object); + + switch (prop_id) { + case PROP_OVERLAY: + g_value_set_uint (value, self->overlay); + break; + case PROP_OVERLAY_DEPTH: + g_value_set_uint (value, self->overlay_depth); + break; + case PROP_OVERLAY_X: + g_value_set_uint (value, self->overlay_x); + break; + case PROP_OVERLAY_Y: + g_value_set_uint (value, self->overlay_y); + break; + case PROP_OVERLAY_W: + g_value_set_uint (value, self->overlay_w); + break; + case PROP_OVERLAY_H: + g_value_set_uint (value, self->overlay_h); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +gboolean +gst_omx_video_sink_start (GstBaseSink * videosink) +{ + GstOmxVideoSink *self = GST_OMX_VIDEO_SINK (videosink); + GstOmxVideoSinkClass *klass = GST_OMX_VIDEO_SINK_GET_CLASS (self); + gint in_port_index; + + GST_DEBUG_OBJECT (self, "opening video renderer"); + + self->sink = + gst_omx_component_new (GST_OBJECT_CAST (self), klass->cdata.core_name, + klass->cdata.component_name, klass->cdata.component_role, + klass->cdata.hacks); + + if (!self->sink) + return FALSE; + + if (gst_omx_component_get_state (self->sink, + GST_CLOCK_TIME_NONE) != OMX_StateLoaded) + return FALSE; + + in_port_index = klass->cdata.in_port_index; + + if (in_port_index == -1) { + OMX_PORT_PARAM_TYPE param; + OMX_ERRORTYPE err; + + GST_OMX_INIT_STRUCT (¶m); + + err = + gst_omx_component_get_parameter (self->sink, OMX_IndexParamVideoInit, + ¶m); + if (err != OMX_ErrorNone) { + GST_WARNING_OBJECT (self, "Couldn't get port information: %s (0x%08x)", + gst_omx_error_to_string (err), err); + /* Fallback */ + in_port_index = 0; + } else { + GST_DEBUG_OBJECT (self, "Detected %lu ports, starting at %lu", + param.nPorts, param.nStartPortNumber); + in_port_index = param.nStartPortNumber + 0; + } + } + self->sink_in_port = gst_omx_component_add_port (self->sink, in_port_index); + + if (!self->sink_in_port) + return FALSE; + + GST_DEBUG_OBJECT (self, "Opened the video renderer"); + + return TRUE; +} + +static gboolean +gst_omx_video_sink_shutdown (GstOmxVideoSink * self) +{ + OMX_STATETYPE state; + + GST_DEBUG_OBJECT (self, "Shutting down renderer"); + + state = gst_omx_component_get_state (self->sink, 0); + + if (state > OMX_StateLoaded || state == OMX_StateInvalid) { + if (state > OMX_StateIdle) { + gst_omx_component_set_state (self->sink, OMX_StateIdle); + gst_omx_component_get_state (self->sink, 5 * GST_SECOND); + } + gst_omx_component_set_state (self->sink, OMX_StateLoaded); + gst_omx_port_deallocate_buffers (self->sink_in_port); + + if (state > OMX_StateLoaded) + gst_omx_component_get_state (self->sink, 5 * GST_SECOND); + } + + return TRUE; +} + +gboolean +gst_omx_video_sink_stop (GstBaseSink * videosink) +{ + GstOmxVideoSink *self = GST_OMX_VIDEO_SINK (videosink); + + GST_DEBUG_OBJECT (self, "Closing renderer"); + + if (!gst_omx_video_sink_shutdown (self)) + return FALSE; + + self->sink_in_port = NULL; + if (self->sink) + gst_omx_component_free (self->sink); + + self->sink = NULL; + + if (self->pool) + gst_object_unref (self->pool); + self->pool = NULL; + + GST_DEBUG_OBJECT (self, "Closed renderer"); + + return TRUE; +} + +static void +gst_omx_video_sink_check_nvfeatures (GstOmxVideoSink * self, GstCaps * caps) +{ + GstCapsFeatures *feature; + feature = gst_caps_get_features (caps, 0); + if (gst_caps_features_contains (feature, "memory:NVMM")) { + self->hw_path = TRUE; + } +} + +static OMX_ERRORTYPE +gstomx_use_allow_secondary_window_extension (GstOmxVideoSink * self) +{ + NVX_CONFIG_ALLOWSECONDARYWINDOW param; + OMX_INDEXTYPE eIndex; + OMX_ERRORTYPE eError = OMX_ErrorNone; + + eError = gst_omx_component_get_index (GST_OMX_VIDEO_SINK (self)->sink, + (char *) NVX_INDEX_CONFIG_ALLOWSECONDARYWINDOW, &eIndex); + if (eError == OMX_ErrorNone) { + GST_OMX_INIT_STRUCT (¶m); + param.nPortIndex = self->sink_in_port->index; + param.bAllow = OMX_TRUE; + eError = + gst_omx_component_set_config (GST_OMX_VIDEO_SINK (self)->sink, + eIndex, + ¶m); + } + + if (eError != OMX_ErrorNone) { + g_error ("Couldnt use the Vendor Extension %s \n", + (char *) NVX_INDEX_CONFIG_ALLOWSECONDARYWINDOW); + } + + return eError; +} + +static OMX_ERRORTYPE +gstomx_use_overlay_index_extension (GstOmxVideoSink * self) +{ + NVX_CONFIG_OVERLAYINDEX param; + OMX_INDEXTYPE eIndex; + OMX_ERRORTYPE eError = OMX_ErrorNone; + + eError = gst_omx_component_get_index (GST_OMX_VIDEO_SINK (self)->sink, + (char *) NVX_INDEX_CONFIG_OVERLAYINDEX, &eIndex); + if (eError == OMX_ErrorNone) { + GST_OMX_INIT_STRUCT (¶m); + param.nPortIndex = self->sink_in_port->index; + param.index = self->overlay; + eError = + gst_omx_component_set_config (GST_OMX_VIDEO_SINK (self)->sink, + eIndex, + ¶m); + } + + if (eError != OMX_ErrorNone) { + g_error ("Couldnt use the Vendor Extension %s \n", + (char *) NVX_INDEX_CONFIG_OVERLAYINDEX); + } + + return eError; +} + +gboolean +gst_omx_video_sink_setcaps (GstBaseSink * sink, GstCaps * caps) +{ + GstOmxVideoSink *self = GST_OMX_VIDEO_SINK (sink); + GstVideoInfo info; + GstBufferPool *newpool, *oldpool; + GstStructure *config; + gint size, min; + gboolean is_format_change = FALSE; + gboolean needs_disable = FALSE; + OMX_PARAM_PORTDEFINITIONTYPE port_def; + + if (!gst_video_info_from_caps (&info, caps)) + goto invalid_format; + + GST_DEBUG_OBJECT (self, "Setting new caps %" GST_PTR_FORMAT, caps); + + gst_omx_port_get_port_definition (self->sink_in_port, &port_def); + gst_omx_video_sink_check_nvfeatures (self, caps); + + /* Check if the caps change is a real format change or if only irrelevant + * parts of the caps have changed or nothing at all. + */ + is_format_change |= port_def.format.video.nFrameWidth != info.width; + is_format_change |= port_def.format.video.nFrameHeight != info.height; + + is_format_change |= (port_def.format.video.xFramerate == 0 && info.fps_n != 0) + || (port_def.format.video.xFramerate != + (info.fps_n << 16) / (info.fps_d)); + + needs_disable = + gst_omx_component_get_state (self->sink, + GST_CLOCK_TIME_NONE) != OMX_StateLoaded; + + if (needs_disable && !is_format_change && !self->sink_in_port->reconfigure) { + GST_DEBUG_OBJECT (self, + "Already running and caps did not change the format"); + return TRUE; + } + + if (needs_disable && (is_format_change || self->sink_in_port->reconfigure)) { + + GstOMXPort *in_port = self->sink_in_port; + + GST_DEBUG_OBJECT (self, "Need to disable renderer"); + + self->sink_in_port->reconfigure = FALSE; + gst_omx_port_set_flushing (in_port, 5 * GST_SECOND, TRUE); + + if (gst_omx_port_set_enabled (self->sink_in_port, FALSE) != OMX_ErrorNone) + return FALSE; + + if (gst_omx_port_wait_buffers_released (self->sink_in_port, + 5 * GST_SECOND) != OMX_ErrorNone) + return FALSE; + + if (gst_omx_port_deallocate_buffers (self->sink_in_port) != OMX_ErrorNone) + return FALSE; + + if (gst_omx_port_wait_enabled (self->sink_in_port, + 1 * GST_SECOND) != OMX_ErrorNone) + return FALSE; + } + + GST_VIDEO_SINK_WIDTH (self) = info.width; + GST_VIDEO_SINK_HEIGHT (self) = info.height; + self->fps_n = info.fps_n; + self->fps_d = info.fps_d; + + port_def.format.video.nFrameWidth = info.width; + port_def.format.video.nFrameHeight = info.height; + if (info.fps_n == 0) + port_def.format.video.xFramerate = 0; + else + port_def.format.video.xFramerate = (info.fps_n << 16) / (info.fps_d); + + port_def.nBufferSize = info.size; + size = info.size; + min = MAX (port_def.nBufferCountMin, 4); + port_def.nBufferCountActual = min; + + GST_DEBUG_OBJECT (self, "Setting inport port definition"); + + if (gst_omx_port_update_port_definition (self->sink_in_port, + &port_def) != OMX_ErrorNone) + return FALSE; + + GST_DEBUG_OBJECT (self, "Enabling component"); + + if (needs_disable) { + if (gst_omx_port_mark_reconfigured (self->sink_in_port) != OMX_ErrorNone) + return FALSE; + } else { + if (gst_omx_port_set_enabled (self->sink_in_port, FALSE) != OMX_ErrorNone) + return FALSE; + + if (gst_omx_port_wait_enabled (self->sink_in_port, + 1 * GST_SECOND) != OMX_ErrorNone) + return FALSE; + + if (gst_omx_component_set_state (self->sink, + OMX_StateIdle) != OMX_ErrorNone) + return FALSE; + + if (gst_omx_component_get_state (self->sink, + GST_CLOCK_TIME_NONE) != OMX_StateIdle) + return FALSE; + + if (gst_omx_component_set_state (self->sink, + OMX_StateExecuting) != OMX_ErrorNone) + return FALSE; + + if (gst_omx_component_get_state (self->sink, + GST_CLOCK_TIME_NONE) != OMX_StateExecuting) + return FALSE; + } + + /* Unset flushing to allow ports to accept data again */ + gst_omx_port_set_flushing (self->sink_in_port, 5 * GST_SECOND, FALSE); + + if (gst_omx_component_get_last_error (self->sink) != OMX_ErrorNone) { + GST_ERROR_OBJECT (self, "Component in error state: %s (0x%08x)", + gst_omx_component_get_last_error_string (self->sink), + gst_omx_component_get_last_error (self->sink)); + return FALSE; + } + + g_mutex_lock (&self->flow_lock); + newpool = + gst_omx_sink_buffer_pool_new (GST_ELEMENT_CAST (self), self->sink, + self->sink_in_port); + + config = gst_buffer_pool_get_config (newpool); + gst_buffer_pool_config_set_params (config, caps, size, min, min); + gst_buffer_pool_config_set_allocator (config, + ((GstOmxSinkBufferPool *) newpool)->allocator, NULL); + if (!gst_buffer_pool_set_config (newpool, config)) + goto config_failed; + + oldpool = self->pool; + self->pool = newpool; + g_mutex_unlock (&self->flow_lock); + + /* unref the old sink */ + if (oldpool) { + gst_object_unref (oldpool); + } + +#ifdef USE_OMX_TARGET_TEGRA + gstomx_use_allow_secondary_window_extension (self); + gstomx_use_overlay_index_extension (self); +#endif + + if (OMX_ErrorNone != Update_Overlay_PlaneBlend (self)) + GST_ERROR_OBJECT (self, "Failed to set Overlay depth"); + + if (OMX_ErrorNone != Update_Overlay_Position (self)) + GST_ERROR_OBJECT (self, "Failed to set Overlay Position"); + + if (OMX_ErrorNone != Update_Overlay_Size (self)) + GST_ERROR_OBJECT (self, "Failed to set Overlay Width"); + + return TRUE; + +config_failed: + { + GST_ERROR_OBJECT (self, "failed to set config."); + g_mutex_unlock (&self->flow_lock); + return FALSE; + } +invalid_format: + { + GST_ERROR_OBJECT (self, "caps invalid"); + return FALSE; + } +} + +GstCaps * +gst_omx_video_sink_getcaps (GstBaseSink * sink, GstCaps * filter) +{ + GstCaps *caps = NULL; + GstOmxVideoSink *self = GST_OMX_VIDEO_SINK (sink); + + caps = gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD (self)); + + if (filter) { + GstCaps *intersection; + + intersection = + gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST); + gst_caps_unref (caps); + caps = intersection; + } + + return caps; +} + +static GstFlowReturn +gst_omx_video_sink_show_frame (GstVideoSink * video_sink, GstBuffer * buf) +{ + + GstOmxVideoSink *self = GST_OMX_VIDEO_SINK (video_sink); + GstOMXBuffer *omxbuf; + GstMemory *mem; + GstFlowReturn res = GST_FLOW_OK; + GstClockTime bufpts = GST_BUFFER_PTS (buf); + OMX_ERRORTYPE err = OMX_ErrorNone; + + GST_DEBUG_OBJECT (self, "Received the frame"); + + /* + if (buf && self->cur_buf != buf) { + if (self->cur_buf) { + GST_LOG_OBJECT (self, "unreffing %p", self->cur_buf); + gst_buffer_unref (self->cur_buf); + } + GST_LOG_OBJECT (self, "reffing %p as our current buffer", buf); + self->cur_buf = gst_buffer_ref (buf); + } + */ + + mem = gst_buffer_peek_memory (buf, 0); + + if (mem + && g_strcmp0 (mem->allocator->mem_type, GST_OMX_SINK_MEMORY_TYPE) == 0) { + /* Buffer from our pool, can be directly released without copy */ + + omxbuf = gst_mini_object_get_qdata (GST_MINI_OBJECT_CAST (buf), + gst_omx_sink_data_quark); + + gst_omx_handle_messages (self->sink); + + /* + * The number of buffers in the decoder output pool is maintained + * using the length of the pending_buffers queue + */ + g_queue_pop_head (&self->sink_in_port->pending_buffers); + + /* + * To prevent deadlock when no buffers are available in the + * decoder output buffer pool, we make sure there is at least + * one buffer in the pool + */ + while (g_queue_get_length (&self->sink_in_port->pending_buffers) == 0) { + g_mutex_lock (&self->sink->messages_lock); + g_cond_wait (&self->sink->messages_cond, &self->sink->messages_lock); + g_mutex_unlock (&self->sink->messages_lock); + gst_omx_handle_messages (self->sink); + } + res = GST_FLOW_OK; + } else { +/* Buffer is not from our pool, copy data */ + + GstMapInfo map = GST_MAP_INFO_INIT; + GstOMXPort *port = self->sink_in_port; + + if (!gst_omx_port_is_enabled (port)) { + err = gst_omx_port_set_enabled (port, TRUE); + if (err != OMX_ErrorNone) { + GST_INFO_OBJECT (self, + "Failed to enable port: %s (0x%08x)", + gst_omx_error_to_string (err), err); + res = GST_FLOW_ERROR; + goto done; + } + + if (gst_omx_port_allocate_buffers (self->sink_in_port) != OMX_ErrorNone) { + res = GST_FLOW_ERROR; + goto done; + } + + if (gst_omx_port_wait_enabled (self->sink_in_port, + 5 * GST_SECOND) != OMX_ErrorNone) { + res = GST_FLOW_ERROR; + goto done; + } + } + + if (gst_omx_port_acquire_buffer (self->sink_in_port, + &omxbuf) != GST_OMX_ACQUIRE_BUFFER_OK) { + res = GST_FLOW_ERROR; + goto done; + } + gst_buffer_map (buf, &map, GST_MAP_READ); + memcpy (omxbuf->omx_buf->pBuffer + omxbuf->omx_buf->nOffset, + map.data, map.size); + gst_buffer_unmap (buf, &map); + } + omxbuf->omx_buf->nFilledLen = mem->size; + omxbuf->gst_buf = gst_buffer_ref (buf); + + if (self->hw_path) + omxbuf->omx_buf->nFlags |= OMX_BUFFERFLAG_NV_BUFFER; + + if (GST_CLOCK_TIME_IS_VALID (bufpts)) + omxbuf->omx_buf->nTimeStamp = + gst_util_uint64_scale (bufpts, OMX_TICKS_PER_SECOND, GST_SECOND); + else + omxbuf->omx_buf->nTimeStamp = 0; + + gst_omx_port_release_buffer (self->sink_in_port, omxbuf); + +done: + + return res; +} + +gboolean +gst_omx_video_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query) +{ + GstOmxVideoSink *omxsink = GST_OMX_VIDEO_SINK (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 (&omxsink->flow_lock); + if ((pool = omxsink->pool)) + gst_object_ref (pool); + g_mutex_unlock (&omxsink->flow_lock); + + if (pool != NULL) { + GstCaps *pcaps; + + /* we had a pool, check caps */ + config = gst_buffer_pool_get_config (pool); + gst_buffer_pool_config_get_params (config, &pcaps, &size, NULL, NULL); + + GST_DEBUG_OBJECT (omxsink, + "we had a pool with caps %" GST_PTR_FORMAT, pcaps); + if (!gst_caps_is_equal (caps, pcaps)) { + /* different caps, we can't use this pool */ + GST_DEBUG_OBJECT (omxsink, "pool has different caps"); + gst_object_unref (pool); + pool = NULL; + } + gst_structure_free (config); + } + if (pool == NULL && need_pool) { + GstVideoInfo info; + + if (!gst_video_info_from_caps (&info, caps)) + goto invalid_caps; + + GST_DEBUG_OBJECT (omxsink, "create new pool"); + pool = + gst_omx_sink_buffer_pool_new (GST_ELEMENT_CAST (omxsink), omxsink->sink, + omxsink->sink_in_port); + + /* the normal size of a frame */ + size = info.size; + + config = gst_buffer_pool_get_config (pool); + gst_buffer_pool_config_set_params (config, caps, size, 2, 4); + gst_buffer_pool_config_set_allocator (config, + ((GstOmxSinkBufferPool *) pool)->allocator, NULL); + if (!gst_buffer_pool_set_config (pool, config)) + goto config_failed; + } + if (pool) { + gst_query_add_allocation_pool (query, pool, size, 2, 4); + gst_query_add_allocation_param (query, + ((GstOmxSinkBufferPool *) pool)->allocator, NULL); + gst_object_unref (pool); + } + gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL); + + 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"); + gst_object_unref (pool); + return FALSE; + } +} + +void +gst_omx_video_sink_get_times (GstBaseSink * sink, GstBuffer * buffer, + GstClockTime * start, GstClockTime * end) +{ + GstOmxVideoSink *self; + + self = GST_OMX_VIDEO_SINK (sink); + + if (GST_BUFFER_PTS_IS_VALID (buffer)) { + *start = GST_BUFFER_PTS (buffer); + if (GST_BUFFER_DURATION_IS_VALID (buffer)) { + *end = *start + GST_BUFFER_DURATION (buffer); + } else { + if (self->fps_n > 0) { + *end = *start + + gst_util_uint64_scale_int (GST_SECOND, self->fps_d, self->fps_n); + } + } + } +} + +static gboolean +gst_omx_video_sink_event (GstBaseSink * sink, GstEvent * event) +{ + + GstOmxVideoSink *omxsink = GST_OMX_VIDEO_SINK (sink); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_CUSTOM_DOWNSTREAM: { + if (gst_event_has_name (event, "ReleaseLastBuffer")) { + GstOMXBuffer *omxbuf; + gst_omx_port_acquire_buffer (omxsink->sink_in_port, &omxbuf); + omxbuf->gst_buf = NULL; + + if (G_LIKELY (omxbuf)) { + omxbuf->omx_buf->nFilledLen = 0; + omxbuf->omx_buf->nFlags |= OMX_BUFFERFLAG_EOS; + gst_omx_port_release_buffer (omxsink->sink_in_port, omxbuf); + } + if (gst_base_sink_is_last_sample_enabled (sink)) { + gst_base_sink_set_last_sample_enabled(sink, FALSE); + gst_base_sink_set_last_sample_enabled(sink, TRUE); + } + gst_omx_port_wait_buffers_released (omxsink->sink_in_port, + 5 * GST_SECOND); + } + break; + } + default: + break; + } + return GST_BASE_SINK_CLASS (parent_class)->event (sink, event); +} |