From 6fcd1dc2d8ad751c7c34982de7b4ddcf75894989 Mon Sep 17 00:00:00 2001 From: svcmobrel-release Date: Thu, 2 May 2024 11:24:58 -0700 Subject: [PATCH] Updating prebuilts and/or headers 15e77cc11e0be2e813a2a51eaa4256b1026dd16b - gst-nvdrmvideosink/gstnvdrmvideosink.h 444ba0e5976368618bd65182f9618af35e0b7946 - gst-nvdrmvideosink/LICENSE.libgstnvdrmvideosink 464f6823a2241ec38d94c786e23d13dd4b4e2a14 - gst-nvdrmvideosink/Makefile 5335db7375d2cee2cf0c02c68879205293dc63e1 - gst-nvdrmvideosink/gstnvdrmvideosink.c 31762975c758c9a6c0a0f2a811d6850440f347b6 - gst-nvdrmvideosink/util/vt_switch.h ffd00e6dfee167cbf1a33f34d4b2c94269bd21b3 - gst-nvdrmvideosink/util/drmutil.c ef0f0a9b3cfbf3d5f2359e9185a75d258201e20a - gst-nvdrmvideosink/util/drmutil.h c318d575f2c7cd3e805b4283f48da5547b152fd6 - gst-nvdrmvideosink/util/vt_switch.c Change-Id: I2762b2db883bd1f94dce4373f201883dfe565519 --- commitFile.txt | 10 + .../LICENSE.libgstnvdrmvideosink | 36 + gst-nvdrmvideosink/Makefile | 74 + gst-nvdrmvideosink/README.txt | 53 + gst-nvdrmvideosink/gstnvdrmvideosink.c | 1255 +++++++++++++++++ gst-nvdrmvideosink/gstnvdrmvideosink.h | 98 ++ gst-nvdrmvideosink/util/drmutil.c | 678 +++++++++ gst-nvdrmvideosink/util/drmutil.h | 86 ++ gst-nvdrmvideosink/util/vt_switch.c | 185 +++ gst-nvdrmvideosink/util/vt_switch.h | 41 + push_info.txt | 1 + 11 files changed, 2517 insertions(+) create mode 100644 commitFile.txt create mode 100644 gst-nvdrmvideosink/LICENSE.libgstnvdrmvideosink create mode 100644 gst-nvdrmvideosink/Makefile create mode 100644 gst-nvdrmvideosink/README.txt create mode 100644 gst-nvdrmvideosink/gstnvdrmvideosink.c create mode 100644 gst-nvdrmvideosink/gstnvdrmvideosink.h create mode 100644 gst-nvdrmvideosink/util/drmutil.c create mode 100644 gst-nvdrmvideosink/util/drmutil.h create mode 100644 gst-nvdrmvideosink/util/vt_switch.c create mode 100644 gst-nvdrmvideosink/util/vt_switch.h create mode 100644 push_info.txt diff --git a/commitFile.txt b/commitFile.txt new file mode 100644 index 0000000..34a196a --- /dev/null +++ b/commitFile.txt @@ -0,0 +1,10 @@ +Updating prebuilts and/or headers + +15e77cc11e0be2e813a2a51eaa4256b1026dd16b - gst-nvdrmvideosink/gstnvdrmvideosink.h +444ba0e5976368618bd65182f9618af35e0b7946 - gst-nvdrmvideosink/LICENSE.libgstnvdrmvideosink +464f6823a2241ec38d94c786e23d13dd4b4e2a14 - gst-nvdrmvideosink/Makefile +5335db7375d2cee2cf0c02c68879205293dc63e1 - gst-nvdrmvideosink/gstnvdrmvideosink.c +31762975c758c9a6c0a0f2a811d6850440f347b6 - gst-nvdrmvideosink/util/vt_switch.h +ffd00e6dfee167cbf1a33f34d4b2c94269bd21b3 - gst-nvdrmvideosink/util/drmutil.c +ef0f0a9b3cfbf3d5f2359e9185a75d258201e20a - gst-nvdrmvideosink/util/drmutil.h +c318d575f2c7cd3e805b4283f48da5547b152fd6 - gst-nvdrmvideosink/util/vt_switch.c diff --git a/gst-nvdrmvideosink/LICENSE.libgstnvdrmvideosink b/gst-nvdrmvideosink/LICENSE.libgstnvdrmvideosink new file mode 100644 index 0000000..0c07fe9 --- /dev/null +++ b/gst-nvdrmvideosink/LICENSE.libgstnvdrmvideosink @@ -0,0 +1,36 @@ +The software listed below is provided under the following terms and +has dependencies upon third-party open source software, including +portions of the GStreamer framework; refer to those software packages +for additional licensing information. + +libgstnvdrmvideosink (libgstnvdrmvideosink.so) + +------------------------------------ + +/* + * Copyright (c) 2017-2020, NVIDIA CORPORATION. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of NVIDIA CORPORATION nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ diff --git a/gst-nvdrmvideosink/Makefile b/gst-nvdrmvideosink/Makefile new file mode 100644 index 0000000..0da9db0 --- /dev/null +++ b/gst-nvdrmvideosink/Makefile @@ -0,0 +1,74 @@ +############################################################################### +# Copyright (c) 2020-2022, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +############################################################################### + +SO_NAME := libgstnvdrmvideosink.so +DEST_DIR ?= /usr/lib/aarch64-linux-gnu/gstreamer-1.0 + +SRCS := gstnvdrmvideosink.c \ + util/drmutil.c \ + util/vt_switch.c + +INCLUDES += -I/usr/lib/aarch64-linux-gnu/glib-2.0/include/ \ + -I./util/ \ + -I/usr/include/ \ + -I/usr/include/libdrm/ \ + -I../ + +INCLUDES += -I/usr/src/jetson_multimedia_api/include/ + +PKGS := glib-2.0 \ + gstreamer-1.0 \ + gstreamer-base-1.0 \ + gstreamer-video-1.0 + +OBJS := $(SRCS:.c=.o) + +CFLAGS := `pkg-config --cflags $(PKGS)` + +LDFLAGS = -Wl,--no-undefined -L/usr/lib/aarch64-linux-gnu/tegra + +LIBS = -lnvbufsurface -lnvbufsurftransform -lEGL -ldrm + +LIBS += `pkg-config --libs $(PKGS)` + +all: $(SO_NAME) + +%.o: %.c + $(CC) -c $< $(CFLAGS) $(INCLUDES) -o $@ + +$(SO_NAME): $(OBJS) + $(CC) -shared -o $(SO_NAME) $(OBJS) $(LIBS) $(LDFLAGS) + +.PHONY: install +install: $(SO_NAME) + cp -vp $(SO_NAME) $(DEST_DIR) + +.PHONY: clean +clean: + rm -rf $(OBJS) $(SO_NAME) diff --git a/gst-nvdrmvideosink/README.txt b/gst-nvdrmvideosink/README.txt new file mode 100644 index 0000000..c3c13d7 --- /dev/null +++ b/gst-nvdrmvideosink/README.txt @@ -0,0 +1,53 @@ +############################################################################### +# +# Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of NVIDIA CORPORATION nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +############################################################################### + +Steps to compile the "gst-nvdrmvideosink" sources: + +1) Install gstreamer related packages using the command: + + sudo apt-get install gstreamer1.0-tools \ + gstreamer1.0-plugins-base gstreamer1.0-plugins-good \ + gstreamer1.0-plugins-bad \ + gstreamer1.0-libav libgstreamer1.0-dev \ + libgstreamer-plugins-base1.0-dev libegl1-mesa-dev \ + libdrm-dev + +2) Extract the package "libgstnvvideosinks_src.tbz2" as follow: + + tar xvjf libgstnvvideosinks_src.tbz2` + +4) cd "gst-nvdrmvideosink" + +5) run "make" to create "libgstnvdrmvideosink.so" + +6) run "make install" to install "libgstnvdrmvideosink.so" in + "/usr/lib/aarch64-linux-gnu/gstreamer-1.0". + +7) run "DEST_DIR= make install" to install at different . diff --git a/gst-nvdrmvideosink/gstnvdrmvideosink.c b/gst-nvdrmvideosink/gstnvdrmvideosink.c new file mode 100644 index 0000000..d4f00c9 --- /dev/null +++ b/gst-nvdrmvideosink/gstnvdrmvideosink.c @@ -0,0 +1,1255 @@ +/* + * Copyright (c) 2017-2022, NVIDIA CORPORATION. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of NVIDIA CORPORATION nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include +#include +#include +#include +#include +#include "gstnvdrmvideosink.h" +#include +#include "util/drmutil.h" +#include +#include +#include +#include + +#ifdef IS_DESKTOP +#define DEFAULT_NVBUF_API_VERSION_NEW TRUE +#else +#define DEFAULT_NVBUF_API_VERSION_NEW FALSE +#endif + +// +// Blocklinear parameters +// +#define NVRM_SURFACE_BLOCKLINEAR_GOB_HEIGHT 8 +#define NVRM_SURFACE_DEFAULT_BLOCK_HEIGHT_LOG2 4 + +GST_DEBUG_CATEGORY_STATIC (gst_nv_drm_video_sink_debug); +#define GST_CAT_DEFAULT gst_nv_drm_video_sink_debug +#ifndef PACKAGE +#define PACKAGE "nvdrmvideosink" +#endif + +/* Filter signals and args */ +enum +{ + LAST_SIGNAL +}; + +enum +{ + PROP_0, + PROP_CONN_ID, + PROP_PLANE_ID, + PROP_SET_MODE, + PROP_OFFSET_X, + PROP_OFFSET_Y, + PROP_COLOR_RANGE +}; + +struct pflip_info +{ + guint vrefresh; + guint refrate; + struct drm_util_fb drm_fb[2]; + guint front_buf; + guint crtc_id; +}; + +typedef enum { + OUTPUT_COLOR_RANGE_FULL, + OUTPUT_COLOR_RANGE_LIMITED, + OUTPUT_COLOR_RANGE_DEFAULT +} ColorRange; + +static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-raw," + "width = (gint) [ 1 , MAX ]," + "height = (gint) [ 1 , MAX ]" + ";" "video/x-raw(memory:NVMM)," + "width = (gint) [ 1 , MAX ]," "height = (gint) [ 1 , MAX ];") + ); + +#define gst_nv_drm_video_sink_parent_class parent_class +G_DEFINE_TYPE (GstNvDrmVideoSink, gst_nv_drm_video_sink, GST_TYPE_VIDEO_SINK); + +static void gst_nv_drm_video_sink_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec); +static void gst_nv_drm_video_sink_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec); +static GstFlowReturn gst_nv_drm_video_sink_show_frame (GstVideoSink * + video_sink, GstBuffer * buf); +static void gst_nv_drm_video_sink_finalize (GObject * object); +static int set_format (GstNvDrmVideoSink * sink); +static int display (GstVideoSink * video_sink); +char *get_format (guint fmt); +int set_color_range (int fd, int crtc_id, uint64_t value); +ColorRange get_color_range (int color_range); + +/* Calculation for BlockHeightLog2 is done similar to the calculation done + * in NvRmSurfaceShrinkBlockDim API + * + * This function calculates the largest blockheight possible by shrinking + * such that the image height is smaller than the proposed blocksize. + */ +static uint32_t +calculateBlockHeightLog2(uint32_t BlockDimLog2, uint32_t ImageDim, uint32_t GobDim) +{ + if (BlockDimLog2 > 0) { + uint32_t proposedBlockSize = GobDim << (BlockDimLog2 - 1); + while (proposedBlockSize >= ImageDim) + { + BlockDimLog2--; + if (BlockDimLog2 == 0) { + break; + } + proposedBlockSize /= 2; + } + } + return BlockDimLog2; +} + +static int +set_format (GstNvDrmVideoSink * sink) +{ + /* Check formats for NVMM */ + gint checkNVMM = -1; + switch (sink->videoFormat) { + + case GST_VIDEO_FORMAT_BGRx: + sink->drm_format = DRM_FORMAT_XRGB8888; + break; + case GST_VIDEO_FORMAT_RGBA: + sink->drm_format = DRM_FORMAT_ABGR8888; + break; + case GST_VIDEO_FORMAT_NV12: + sink->drm_format = DRM_FORMAT_NV12; + break; + case GST_VIDEO_FORMAT_I420: + sink->drm_format = DRM_FORMAT_YUV420; + break; + case GST_VIDEO_FORMAT_Y444: + sink->drm_format = DRM_FORMAT_YUV444; + break; + case GST_VIDEO_FORMAT_RGBx: + sink->drm_format = DRM_FORMAT_XBGR8888; + break; + case GST_VIDEO_FORMAT_BGRA: + sink->drm_format = DRM_FORMAT_ARGB8888; + break; + case GST_VIDEO_FORMAT_NV16: + sink->drm_format = DRM_FORMAT_NV16; + break; + case GST_VIDEO_FORMAT_NV61: + sink->drm_format = DRM_FORMAT_NV61; + break; + case GST_VIDEO_FORMAT_YV12: + sink->drm_format = DRM_FORMAT_YVU420; + break; + case GST_VIDEO_FORMAT_UYVY: + sink->drm_format = DRM_FORMAT_UYVY; + break; + case GST_VIDEO_FORMAT_YUY2: + sink->drm_format = DRM_FORMAT_YUYV; + break; + case GST_VIDEO_FORMAT_NV21: + sink->drm_format = DRM_FORMAT_NV21; + checkNVMM = 1; + break; + case GST_VIDEO_FORMAT_NV24: + sink->drm_format = DRM_FORMAT_NV24; + break; + case GST_VIDEO_FORMAT_Y42B: + sink->drm_format = DRM_FORMAT_YUV422; + checkNVMM = 1; + break; + default: + GST_ERROR ("Video format not supported. \n"); + return 0; + } + + /* + * Check for formats that are supported only by NvBuffers, + * and not software buffers + */ + if (checkNVMM == 1) { + if (sink->using_NVMM) { + return 1; + } else { + return 0; + } + } + + return 1; +} + +static int +display (GstVideoSink * video_sink) +{ + GstNvDrmVideoSink *sink = GST_NVDRMVIDEOSINK (video_sink); + + /* If sink wants to set mode */ + if (sink->set_mode) { + /* Display NVMM buffer */ + if (sink->using_NVMM) { + if (drmModePageFlip (sink->fd, sink->crtc_id, + sink->buf_id[sink->frame_count], DRM_MODE_PAGE_FLIP_EVENT, + NULL)) { + g_print ("Failed to page flip \n"); + return 1; + } + } else { + if (drmModePageFlip (sink->fd, sink->crtc_id, + sink->fb[sink->frame_count].fb_id, DRM_MODE_PAGE_FLIP_EVENT, + NULL)) { + g_print ("Failed to page flip \n"); + return 1; + } + } + + drmEventContext evctx; + + /* Set up our event handler */ + memset(&evctx, 0, sizeof evctx); + evctx.version = DRM_EVENT_CONTEXT_VERSION; + evctx.vblank_handler = NULL; + evctx.page_flip_handler = NULL; + + + struct timeval timeout; + timeout.tv_sec = 3; + timeout.tv_usec = 0; + fd_set fds; + FD_ZERO (&fds); + FD_SET (sink->fd, &fds); + + int ret = select (sink->fd + 1, &fds, NULL, NULL, &timeout); + + if (ret > 0) { + drmHandleEvent(sink->fd, &evctx); + } else if(ret == 0) { + g_print ("timeout reached before any flip call occurred\n"); + } else { + g_print ("select failed with error message : %s\n",strerror(errno)); + } + } else { + guint crtc_id = sink->crtc_id; + guint pl_id = sink->plane_id; + guint nvheight = sink->height; + guint nvwidth = sink->width; + + if (sink->using_NVMM) { + if (drmModeSetPlane (sink->fd, pl_id, crtc_id, + sink->buf_id[sink->frame_count], 0, sink->offset_x, + sink->offset_y, nvwidth, nvheight, 0, 0, (nvwidth) << 16, + (nvheight) << 16)) { + g_print ("Failed to set plane \n"); + return 1; + } + } else { + if (drmModeSetPlane (sink->fd, pl_id, crtc_id, + sink->fb[sink->frame_count].fb_id, 0, sink->offset_x, + sink->offset_y, nvwidth, nvheight, 0, 0, nvwidth << 16, + nvheight << 16)) { + g_print ("Failed to set plane \n"); + return 1; + } + } + } + return 0; +} + +static gboolean +gst_nvdrmvideosink_propose_allocation (GstBaseSink * bsink, GstQuery * query) +{ + GstNvDrmVideoSink *sink = GST_NVDRMVIDEOSINK (bsink); + + if (sink->last_buf) { + gst_buffer_unref (sink->last_buf); + sink->last_buf = NULL; + sink->is_drc_on = 1; + } + + return TRUE; +} + +static GstFlowReturn +gst_nv_drm_video_sink_show_frame (GstVideoSink * video_sink, GstBuffer * buf) +{ + GstFlowReturn res = GST_FLOW_OK; + GstNvDrmVideoSink *sink = GST_NVDRMVIDEOSINK (video_sink); + sink->frame_count ^= 1; + GST_DEBUG ("New frame received \n"); + + GstMemory *mem; + GstMapInfo map = { NULL, (GstMapFlags) 0, NULL, 0, 0, }; + mem = gst_buffer_peek_memory (buf, 0); + gst_memory_map (mem, &map, GST_MAP_READ); + + /* Check for type of buffer */ + if ((sink->using_NVMM) == 1) { + + GST_DEBUG_OBJECT (sink, "NVMM buffer processing \n"); + + /* Processing frame and rendering using DRM */ + + /* Plane information */ + guint nvhmem[NVBUF_MAX_PLANES] = { 0 }; + guint nvblocksize[NVBUF_MAX_PLANES] = { 0 }; + static guint handle[NVBUF_MAX_PLANES] = { 0 }; + static guint nvhandle = 0; + static guint bo_handles[NVBUF_MAX_PLANES] = { 0 }; + static uint64_t modifiers[NVBUF_MAX_PLANES] = { 0 }; + static guint pitches[NVBUF_MAX_PLANES] = { 0 }; + static guint offsets[NVBUF_MAX_PLANES] = { 0 }; + guint width = 0, height = 0; + gint num_planes = 0; + gint ret = -1; + NvBufSurface *in_surface = NULL; + + in_surface = (NvBufSurface *) map.data; + NvBufSurfaceMemType memType = in_surface->memType; + gst_memory_unmap (mem, &map); + + if (memType == NVBUF_MEM_DEFAULT || memType == NVBUF_MEM_SURFACE_ARRAY || memType == NVBUF_MEM_HANDLE) { + num_planes = (int)in_surface->surfaceList[0].planeParams.num_planes; + gint hm = 0; + for (hm = 0; hm < num_planes; hm++) { + nvhmem[hm] = in_surface->surfaceList[0].bufferDesc; + nvblocksize[hm] = calculateBlockHeightLog2(NVRM_SURFACE_DEFAULT_BLOCK_HEIGHT_LOG2, + in_surface->surfaceList[0].planeParams.height[hm], + NVRM_SURFACE_BLOCKLINEAR_GOB_HEIGHT); + + ret = drmPrimeFDToHandle (sink->fd, nvhmem[hm], &nvhandle); + if (ret < 0) { + g_print ("drmPrimeFDToHandle call failed \n"); + return GST_FLOW_ERROR; + } else { + handle[hm] = nvhandle; + } + + if (sink->is_tegra_drm) { + if (in_surface->surfaceList[0].layout == NVBUF_LAYOUT_BLOCK_LINEAR) { + //Set sector layout bit + modifiers[hm] = DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(nvblocksize[hm]); + modifiers[hm] |= 1 << 22; + } else { + modifiers[hm] = DRM_FORMAT_MOD_LINEAR; + } + } else if (sink->is_nvidia_drm) { + // nvidia drm is used on T23x or later + if (in_surface->surfaceList[0].layout == NVBUF_LAYOUT_BLOCK_LINEAR) { + // The kindGen and block linear kind is as below. + modifiers[hm] = DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 2, 0x06, nvblocksize[hm]); + } else { + modifiers[hm] = DRM_FORMAT_MOD_LINEAR; + } + } else { + if (in_surface->surfaceList[0].layout == NVBUF_LAYOUT_BLOCK_LINEAR) { + gint gset = gem_set_params (sink->fd, handle[hm], nvblocksize[hm]); + if (gset < 0) { + g_print ("Failed to set parameters of block linear data \n"); + return GST_FLOW_ERROR; + } + } + } + } + + for (hm = 0; hm < num_planes; hm++) { + bo_handles[hm] = handle[hm]; + /* store previous handles */ + sink->drm_bo_handles[sink->frame_count][hm] = handle[hm]; + pitches[hm] = in_surface->surfaceList[0].planeParams.pitch[hm]; + offsets[hm] = in_surface->surfaceList[0].planeParams.offset[hm]; + } + + width = in_surface->surfaceList[0].planeParams.width[0]; + height = in_surface->surfaceList[0].planeParams.height[0]; + } + + /* Create fb */ + if (sink->is_tegra_drm || sink->is_nvidia_drm) { + if (drmModeAddFB2WithModifiers (sink->fd, width, height, + sink->drm_format, bo_handles, pitches, offsets, + modifiers, &(sink->buf_id[sink->frame_count]), + DRM_MODE_FB_MODIFIERS)) { + g_print ("Failed to create frame buffer\n"); + return GST_FLOW_ERROR; + } + } + else { + if (drmModeAddFB2 (sink->fd, width, height, + sink->drm_format, bo_handles, pitches, offsets, + &(sink->buf_id[sink->frame_count]), 0)) { + g_print ("Failed to create frame buffer\n"); + return GST_FLOW_ERROR; + } + } + + /* Display fb */ + if (display (video_sink)) { + g_print ("Failed to display frame buffer\n"); + res = GST_FLOW_ERROR; + } + + /* Release the last buffer held */ + if (sink->last_buf) { + gst_buffer_unref (sink->last_buf); + sink->last_buf = NULL; + } + + /* Remove previous frame buffer to avoid overflow */ + if (sink->buf_id[sink->frame_count ^ 1]) { + /* remove bo handles as well whose ref_count has become 2 */ + gint k = 0; + for (k = 0; k < num_planes; k++) { + drm_util_close_gem_bo (sink->fd, + sink->drm_bo_handles[sink->frame_count ^ 1][k]); + } + if (drmModeRmFB (sink->fd, sink->buf_id[sink->frame_count ^ 1])) { + g_print ("Cannot remove frame buffer \n"); + return FALSE; + } + } + + /* Save and hold the current buffer to prevent decoder to over write it */ + if (!sink->is_drc_on) { + sink->last_buf = buf; + gst_buffer_ref (sink->last_buf); + } + } else { + + GST_DEBUG_OBJECT (sink, "Software buffer processing \n"); + + /* Processing frame and rendering using DRM */ + guint8 *inputBuf; + inputBuf = map.data; + guint sizeInput = (guint) map.size; + gst_memory_unmap (mem, &map); + + /* Fill data from input buffer to drm frame buffer */ + if (sizeInput) { + if (drm_util_fill_data (&(sink->fb[sink->frame_count]), inputBuf, + sizeInput)) { + GST_ERROR (" Cannot fill frame buffer \n"); + return GST_FLOW_ERROR; + } + } else { + GST_ERROR (" Input buffer is empty \n"); + return GST_FLOW_ERROR; + } + + /* display fb */ + if (display (video_sink)) { + GST_ERROR ("Failed to display frame buffer\n"); + return GST_FLOW_ERROR; + } + } + return res; +} + +static GstCaps * +gst_nvdrmvideosink_get_caps (GstBaseSink * base_sink, GstCaps * filter) +{ + GstNvDrmVideoSink *sink = (GstNvDrmVideoSink *) (base_sink); + GstCaps *tmp = NULL; + GstCaps *result = NULL; + + GST_DEBUG_OBJECT (sink, "Received caps %" GST_PTR_FORMAT, filter); + + tmp = gst_pad_get_pad_template_caps (GST_BASE_SINK_PAD (base_sink)); + + if (filter) { + GST_DEBUG_OBJECT (base_sink, + "Intersecting with filter caps %" GST_PTR_FORMAT, filter); + + result = gst_caps_intersect_full (filter, tmp, GST_CAPS_INTERSECT_FIRST); + gst_caps_unref (tmp); + } else { + result = tmp; + } + + if (sink->outcaps) { + tmp = result; + result = gst_caps_intersect (tmp, sink->outcaps); + gst_caps_unref (tmp); + } + + GST_DEBUG_OBJECT (base_sink, "Returning caps: %" GST_PTR_FORMAT, result); + + return result; + +} + +static gboolean +gst_nvdrmvideosink_set_caps (GstBaseSink * base_sink, GstCaps * caps) +{ + gboolean ret = TRUE; + + GstNvDrmVideoSink *sink = (GstNvDrmVideoSink *) (base_sink); + GstVideoInfo info; + GstCapsFeatures *features; + + GST_DEBUG_OBJECT (sink, "Received caps %" GST_PTR_FORMAT, caps); + + /* Extract information from the source */ + if (!gst_video_info_from_caps (&info, caps)) + goto invalid_format; + + features = gst_caps_get_features (caps, 0); + + if (gst_caps_features_contains (features, "memory:NVMM")) { + sink->using_NVMM = 1; + } else { + sink->using_NVMM = 0; + } + + gint is_res_changed = 0; + if ((sink->width != 0 && sink->height != 0) && (sink->width != info.width && sink->height != info.height)) { + is_res_changed = 1; + } + + if (is_res_changed) { + sink->is_drc_on = 0; + } + + sink->width = info.width; + sink->height = info.height; + sink->fps_n = info.fps_n; + sink->fps_d = info.fps_d; + sink->videoFormat = GST_VIDEO_FORMAT_INFO_FORMAT (info.finfo); + + /* Convert gst video format to drm format */ + gint fret = set_format (sink); + + if (!fret) { + GST_ERROR ("Video format not supported. \n"); + return GST_FLOW_NOT_SUPPORTED; + } + + gint m; + /* create fb */ + if (!(sink->using_NVMM)) { + for (m = 0; m < 2; m++) { + if (!drm_util_create_dumb_fb (sink->fd, + sink->width, sink->height, sink->drm_format, &(sink->fb[m]))) { + g_print ("Cannot create frame buffer \n"); + return FALSE; + } + } + } + + /* If user wants to set mode */ + if (sink->set_mode) { + //Add color range property + ColorRange output_color_range = get_color_range(sink->color_range); + if (!set_color_range (sink->fd, sink->crtc_id, output_color_range)) { + GST_ERROR ("unable to set color range property\n"); + } + /* Store default ctrc props */ + sink->default_crtcProp = drmModeGetCrtc (sink->fd, sink->crtc_id); + + /* Check if incoming caps intersects with the caps created with modes */ + if (gst_caps_can_intersect (caps, sink->outcaps)) { + gint m; + drmModeModeInfoPtr mode = NULL; + for (m = 0; m < sink->num_modes; m++) { + mode = &(sink->conn_info)->modes[m]; + if ((info.width == mode->hdisplay) && (info.height == mode->vdisplay)) { + sink->mode = mode; + break; + } + } + + if (m == sink->num_modes) { + GST_ERROR_OBJECT (sink, "Mode not found\n"); + return FALSE; + } + } else { + GST_ERROR_OBJECT (sink, "Mode not supported by connector \n"); + return FALSE; + } + + guint conn_id = sink->conn_id; + guint fb_id = sink->fb[0].fb_id; + + if (sink->using_NVMM) { + if (!drm_util_create_dumb_fb (sink->fd, + sink->mode->hdisplay, sink->mode->vdisplay, sink->drm_format, &(sink->dumb))) { + g_print ("Cannot create frame buffer \n"); + return FALSE; + } + fb_id = sink->dumb.fb_id; + } + + /* Mode setting */ + if (drmModeSetCrtc (sink->fd, + sink->crtc_id, fb_id, sink->is_nvidia_drm ? 0 : sink->offset_x, + sink->is_nvidia_drm ? 0 : sink->offset_y, &conn_id, 1, sink->mode)) { + GST_ERROR_OBJECT (sink, "Set crtc failed \n"); + return FALSE; + } else { + GST_DEBUG_OBJECT (sink, "Set crtc passed \n"); + } + return ret; + } else { + + /* TODO: For a different resolution video, aspect ratio can be calculated and handled later. */ + GST_DEBUG ("Do nothing \n"); + + + if (sink->is_nvidia_drm) { + /* we need a mode to be set for upstream DRM driver */ + drmModeModeInfoPtr mode = NULL; + int area = 0, current_area = 0; + + /* Find the mode with highest resolution */ + for (int i = 0; i < sink->conn_info->count_modes; i++) { + drmModeModeInfoPtr current_mode = &(sink->conn_info)->modes[i]; + current_area = current_mode->hdisplay * current_mode->vdisplay; + if (current_area > area) { + mode = current_mode; + area = current_area; + } + } + + guint conn_id = sink->conn_id; + + if (!drm_util_create_dumb_fb (sink->fd, + mode->hdisplay, mode->vdisplay, sink->drm_format, &(sink->dumb))) { + g_print ("Cannot create frame buffer \n"); + return FALSE; + } + + if (drmModeSetCrtc (sink->fd, + sink->crtc_id, sink->dumb.fb_id, 0, 0, &conn_id, 1, mode)) { + GST_ERROR_OBJECT (sink, "Set crtc failed \n"); + return FALSE; + } + } + + /* Retrieve the default mode of connector */ + drmModeCrtcPtr crtcProp = drmModeGetCrtc (sink->fd, sink->crtc_id); + sink->mode = &(crtcProp->mode); + + if (sink->color_range != 2) { + g_print ("color_range can be set only with set-mode enabled. Please try with set-mode=1 property set\n"); + } + + drmModeFreeCrtc (crtcProp); + + return ret; + } + +invalid_format: + GST_ERROR_OBJECT (sink, "caps invalid"); + + return ret; +} + +static void +add_format_list (GValue * list, char *s) +{ + GValue item = { 0, }; + g_value_init (&item, G_TYPE_STRING); + g_value_set_string (&item, s); + gst_value_list_append_value (list, &item); + g_value_unset (&item); +} + +static void +add_mode_resolution_list (GValue * list, gint i) +{ + GValue item = { 0, }; + g_value_init (&item, G_TYPE_INT); + g_value_set_int (&item, i); + gst_value_list_append_value (list, &item); + g_value_unset (&item); +} + +char * +get_format (guint fmt) +{ + switch (fmt) { + + case (DRM_FORMAT_ARGB1555): + return ("ARGB"); + + case (DRM_FORMAT_XRGB8888): + return ("BGRx"); + + case (DRM_FORMAT_XBGR8888): + return ("RGBx"); + + case (DRM_FORMAT_ARGB8888): + return ("BGRA"); + + case (DRM_FORMAT_ABGR8888): + return ("RGBA"); + + case (DRM_FORMAT_NV12): + return ("NV12"); + + case (DRM_FORMAT_NV21): + return ("NV21"); + + case (DRM_FORMAT_NV16): + return ("NV16"); + + case (DRM_FORMAT_NV61): + return ("NV61"); + + case (DRM_FORMAT_NV24): + return ("NV24"); + + case (DRM_FORMAT_YUV420): + return ("I420"); + + case (DRM_FORMAT_YVU420): + return ("YV12"); + + case (DRM_FORMAT_YUV422): + return ("YUV422"); + + case (DRM_FORMAT_YVU422): + return ("YVU420"); + + case (DRM_FORMAT_YUV444): + return ("Y444"); + + case (DRM_FORMAT_UYVY): + return ("UYVY"); + + case (DRM_FORMAT_YUYV): + return ("YUY2"); + + default: + return NULL; + } +} + +ColorRange get_color_range (int color_range) +{ + switch(color_range) { + case 0: + return OUTPUT_COLOR_RANGE_FULL; + case 1: + return OUTPUT_COLOR_RANGE_LIMITED; + default: + return OUTPUT_COLOR_RANGE_DEFAULT; + } +} + +int set_color_range (int fd, int crtc_id, uint64_t value) +{ + if (value == OUTPUT_COLOR_RANGE_DEFAULT) { + g_print ("color_range if supported, to be set to default by DRM\n"); + return 1; + } + drmModeObjectPropertiesPtr crtc_props; + drmModePropertyRes *prop = NULL; + crtc_props = drmModeObjectGetProperties (fd, crtc_id, DRM_MODE_OBJECT_CRTC); + uint32_t i = 0; + + if (!crtc_props) { + goto fail; + } + + for (i = 0; i < crtc_props->count_props; i++) { + prop = drmModeGetProperty(fd, crtc_props->props[i]); + if (!prop) { + continue; + } + if(!strncmp(prop->name, "OutputColorRange", DRM_PROP_NAME_LEN)) { + break; + } + drmModeFreeProperty(prop); + } + + if (i == crtc_props->count_props) { + GST_ERROR ("Output color range not supported"); + goto fail; + } + + drmModeFreeObjectProperties(crtc_props); + + if (drmModeObjectSetProperty (fd, crtc_id, DRM_MODE_OBJECT_CRTC, prop->prop_id, (uint64_t) value)) { + GST_ERROR ("Failed to set property"); + goto fail; + } + + if (prop) { + drmModeFreeProperty(prop); + } + + return 1; + +fail: + drmModeFreeObjectProperties (crtc_props); + if (prop) { + drmModeFreeProperty(prop); + } + return 0; +} + + +static gboolean +gst_nvdrmvideosink_start (GstBaseSink * base_sink) +{ + GstNvDrmVideoSink *sink = (GstNvDrmVideoSink *) (base_sink); + + sink->vtinfo.console_fd = -1; + sink->vtinfo.active_vt = -1; + /* Load libdrm.so and read in all resources; populate info */ + if (!drm_util_init (&sink->fd, &sink->conn_info, &sink->conn_id, + &sink->crtc_id, &sink->plane_id, &sink->vtinfo, sink->do_vtswitch, + &sink->is_nvidia_drm, &sink->is_tegra_drm)) { + GST_ERROR ("drm_util_init failed\n"); + if (sink->do_vtswitch) { + release_vt(&sink->vtinfo); + } + return FALSE; + } else { + GST_DEBUG ("drm_util_init passed\n"); + } + + sink->frame_count = 0; + sink->num_modes = sink->conn_info->count_modes; + drmModeModeInfoPtr mode = NULL; + GstCaps *caps_fin = NULL, *copy1; + + drmModePlanePtr plane_info = drmModeGetPlane (sink->fd, sink->plane_id); + + if (!plane_info) { + GST_ERROR ("Unable to get plane info\n"); + return 0; + } + + /* Create new caps for available modes of connector + * only if user wants to set mode + */ + if (sink->set_mode) { + caps_fin = gst_caps_new_empty (); + GstCaps *caps_tmp = gst_caps_new_simple ("video/x-raw", + "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, + NULL); + + GValue list = { 0, }, list_w = { 0, }, list_h = { 0, }; + g_value_init (&list, GST_TYPE_LIST); + g_value_init (&list_w, GST_TYPE_LIST); + g_value_init (&list_h, GST_TYPE_LIST); + + guint f = 0; + for (f = 0; f < plane_info->count_formats; f++) { + guint tmp_fmt = *(&plane_info->formats[f]); + add_format_list (&list, get_format (tmp_fmt)); + } + GstStructure *structure = gst_caps_get_structure (caps_tmp, 0); + gst_structure_set_value (structure, "format", &list); + + gint m = 0; + for (m = 0; m < sink->num_modes; m++) { + mode = &(sink->conn_info)->modes[m]; + add_mode_resolution_list (&list_w, mode->hdisplay); + add_mode_resolution_list (&list_h, mode->vdisplay); + } + gst_structure_set_value (structure, "width", &list_w); + gst_structure_set_value (structure, "height", &list_h); + + gst_caps_append (caps_fin, caps_tmp); + + copy1 = gst_caps_copy (caps_fin); + + gint n = gst_caps_get_size (caps_fin); + gint ic; + for (ic = 0; ic < n; ic++) { + GstCapsFeatures *tmp_f = gst_caps_features_new ("memory:NVMM", NULL); + gst_caps_set_features (caps_fin, ic, tmp_f); + } + gst_caps_append (caps_fin, copy1); + + /* Save created caps */ + sink->outcaps = gst_caps_copy (caps_fin); + + /* Free resources */ + g_value_unset (&list); + g_value_unset (&list_w); + g_value_unset (&list_h); + } + /* Free resources */ + drmModeFreePlane (plane_info); + if (caps_fin) { + gst_caps_unref (caps_fin); + } + + return TRUE; +} + +static GstStateChangeReturn +gst_nvdrmvideosink_change_state (GstElement * element, GstStateChange transition) +{ + GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; + GstNvDrmVideoSink *sink = NULL; + + sink = GST_NVDRMVIDEOSINK (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_PLAYING: + break; + case GST_STATE_CHANGE_PLAYING_TO_PAUSED: + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + break; + case GST_STATE_CHANGE_READY_TO_NULL: + if (sink->using_NVMM) { + if (sink->last_buf) { + gst_buffer_unref(sink->last_buf); + sink->last_buf = NULL; + } + + const GstVideoFormatInfo *videoFormatInfo = gst_video_format_get_info(sink->videoFormat); + guint k = 0; + for (k = 0; k < videoFormatInfo->n_planes; k++) { + drm_util_close_gem_bo (sink->fd, + sink->drm_bo_handles[sink->frame_count][k]); + } + } + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + break; + case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + break; + case GST_STATE_CHANGE_PLAYING_TO_PAUSED: + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + break; + case GST_STATE_CHANGE_READY_TO_NULL: + break; + default: + break; + } + return ret; +} + +static gboolean +gst_nvdrmvideosink_stop (GstBaseSink * base_sink) +{ + GstNvDrmVideoSink *sink = (GstNvDrmVideoSink *) (base_sink); + + /* remove frame buffers to avoid overflow */ + if (sink->using_NVMM) { + drmModeRmFB (sink->fd, sink->buf_id[sink->frame_count]); + drmModeRmFB (sink->fd, sink->buf_id[sink->frame_count ^ 1]); + } else { + drmModeRmFB (sink->fd, sink->fb[sink->frame_count].fb_id); + drmModeRmFB (sink->fd, sink->fb[sink->frame_count ^ 1].fb_id); + } + + if (sink->dumb.fb_id != 0) { + drm_util_destroy_dumb_fb(sink->fd, &(sink->dumb)); + } + + if (sink->outcaps) { + gst_caps_unref (sink->outcaps); + } + /* Set NULL window on EOS */ + if (sink->set_mode && sink->default_crtcProp) { + /* Restore mode to the default mode of the connector */ + sink->mode = &((sink->default_crtcProp)->mode); + drmModeFreeCrtc (sink->default_crtcProp); + + guint conn_id = sink->conn_id; + + if (drmModeSetCrtc (sink->fd, sink->crtc_id, 0, 0, 0, &conn_id, 1, sink->mode)) { + GST_ERROR_OBJECT (sink, "Set crtc to NULL failed\n"); + return FALSE; + } else { + GST_DEBUG_OBJECT (sink, "Set crtc to NULL passed \n"); + } + } else { + guint crtc_id = sink->crtc_id; + guint pl_id = sink->plane_id; + guint nvheight = sink->height; + guint nvwidth = sink->width; + + if (drmModeSetPlane (sink->fd, pl_id, crtc_id, + 0, 0, 0, 0, nvwidth, + nvheight, 0, 0, (nvwidth) << 16, (nvheight) << 16)) { + GST_ERROR_OBJECT (sink, "Set plane to NULL failed\n"); + return FALSE; + } else { + GST_DEBUG_OBJECT (sink, "Set plane to NULL passed \n"); + } + } + + drmModeFreeConnector (sink->conn_info); + drmClose (sink->fd); + + if (sink->do_vtswitch) { + release_vt(&sink->vtinfo); + } + + return TRUE; +} + +static gboolean +gst_nv_drm_video_sink_event (GstBaseSink * gst_base, GstEvent * event) +{ + GstNvDrmVideoSink *sink = GST_NVDRMVIDEOSINK (gst_base); + gboolean ret = TRUE; + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_EOS: + /* release last frame */ + if (sink->last_buf) { + gst_buffer_unref (sink->last_buf); + sink->last_buf = NULL; + } + break; + default: + break; + } + + if (GST_BASE_SINK_CLASS (parent_class)->event) { + ret = GST_BASE_SINK_CLASS (parent_class)->event (gst_base, event); + } else { + gst_event_unref (event); + } + + return ret; +} + +static void +gst_nv_drm_video_sink_class_init (GstNvDrmVideoSinkClass * klass) +{ + + GObjectClass *gobject_class = (GObjectClass *) klass; + GstElementClass *gstelement_class = (GstElementClass *) klass; + GstBaseSinkClass *base_sink_class = GST_BASE_SINK_CLASS (klass); + GstVideoSinkClass *videosink_class = GST_VIDEO_SINK_CLASS (klass); + + gobject_class->set_property = gst_nv_drm_video_sink_set_property; + gobject_class->get_property = gst_nv_drm_video_sink_get_property; + gobject_class->finalize = gst_nv_drm_video_sink_finalize; + + gstelement_class->change_state = gst_nvdrmvideosink_change_state; + + base_sink_class->set_caps = GST_DEBUG_FUNCPTR (gst_nvdrmvideosink_set_caps); + base_sink_class->get_caps = GST_DEBUG_FUNCPTR (gst_nvdrmvideosink_get_caps); + + base_sink_class->start = GST_DEBUG_FUNCPTR (gst_nvdrmvideosink_start); + base_sink_class->stop = GST_DEBUG_FUNCPTR (gst_nvdrmvideosink_stop); + base_sink_class->event = GST_DEBUG_FUNCPTR (gst_nv_drm_video_sink_event); + + base_sink_class->propose_allocation = GST_DEBUG_FUNCPTR (gst_nvdrmvideosink_propose_allocation); + + videosink_class->show_frame = + GST_DEBUG_FUNCPTR (gst_nv_drm_video_sink_show_frame); + + g_object_class_install_property (gobject_class, PROP_CONN_ID, + g_param_spec_int ("conn_id", "CONN_ID", + "Sets CONN ID.", 0, INT_MAX, INT_MAX, + (GParamFlags) (G_PARAM_READWRITE))); + + g_object_class_install_property (gobject_class, PROP_PLANE_ID, + g_param_spec_int ("plane_id", "PLANE_ID", + "Sets PLANE ID", 0, INT_MAX, INT_MAX, + (GParamFlags) (G_PARAM_READWRITE))); + + g_object_class_install_property (gobject_class, PROP_SET_MODE, + g_param_spec_boolean ("set_mode", "SET_MODE", + "Selects whether user wants to choose the default mode which is \n" + "\t\t\talready set by connector (set_mode = 0) or wants to select the mode \n" + "\t\t\tof the video stream (set_mode = 1). In the latter case, error is \n" + "\t\t\tthrown when the input stream resolution does not match with \n" + "\t\t\tthe supported modes of the connector. ", + FALSE, (GParamFlags) (G_PARAM_READWRITE))); + + g_object_class_install_property (gobject_class, PROP_OFFSET_X, + g_param_spec_int ("offset_x", "OFFSET_X", + "Sets offset x", 0, INT_MAX, INT_MAX, + (GParamFlags) (G_PARAM_READWRITE))); + + g_object_class_install_property (gobject_class, PROP_OFFSET_Y, + g_param_spec_int ("offset_y", "OFFSET_Y", + "Sets offset y", 0, INT_MAX, INT_MAX, + (GParamFlags) (G_PARAM_READWRITE))); + + g_object_class_install_property (gobject_class, PROP_COLOR_RANGE, + g_param_spec_int ("color_range", "COLOR_RANGE", + "Sets color range only when set-mode = 1\n" + "\t\t\t color_range = 0 - FULL\n" + "\t\t\t color_range = 1 - LIMITED\n" + "\t\t\t color_range = 2 - DEFAULT\n", + 0, 2, 2, + (GParamFlags) (G_PARAM_READWRITE))); + + gst_element_class_set_static_metadata (GST_ELEMENT_CLASS (klass), + "Nvidia Drm Video Sink", + "Video Sink", + "Nvidia Drm Video Sink", "Ashwini Munje "); + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&sink_factory)); +} + +static void +gst_nv_drm_video_sink_init (GstNvDrmVideoSink * sink) +{ + sink->width = 0; + sink->height = 0; + sink->fps_n = 0; + sink->fps_d = 0; + sink->videoFormat = GST_VIDEO_FORMAT_UNKNOWN; + sink->plane_id = INT_MAX; + sink->conn_id = INT_MAX; + sink->crtc_id = INT_MAX; + sink->set_mode = FALSE; + sink->offset_x = 0; + sink->offset_y = 0; + sink->color_range = 2; + sink->using_NVMM = -1; + sink->outcaps = NULL; + sink->do_vtswitch = 0; + return; +} + +static void +gst_nv_drm_video_sink_set_property (GObject * object, guint prop_id, +const GValue * value, GParamSpec * pspec) +{ + + GstNvDrmVideoSink *sink = GST_NVDRMVIDEOSINK (object); + switch (prop_id) { + case PROP_CONN_ID: + sink->conn_id = g_value_get_int (value); + GST_DEBUG_OBJECT (sink, "CONN ID : %d\n", sink->conn_id); + break; + case PROP_PLANE_ID: + sink->plane_id = g_value_get_int (value); + GST_DEBUG_OBJECT (sink, "PLANE ID : %d\n", sink->plane_id); + break; + case PROP_SET_MODE: + sink->set_mode = g_value_get_boolean (value); + GST_DEBUG_OBJECT (sink, "MODE_SET : %d\n", sink->set_mode); + break; + case PROP_OFFSET_X: + sink->offset_x = g_value_get_int (value); + GST_DEBUG_OBJECT (sink, "OFFSET_X : %d\n", sink->offset_x); + break; + case PROP_OFFSET_Y: + sink->offset_y = g_value_get_int (value); + GST_DEBUG_OBJECT (sink, "OFFSET_Y : %d\n", sink->offset_y); + break; + case PROP_COLOR_RANGE: + sink->color_range = g_value_get_int (value); + sink->do_vtswitch = 1; + GST_DEBUG_OBJECT (sink, "COLOR_RANGE : %d\n", sink->color_range); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } + return; +} + +static void +gst_nv_drm_video_sink_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstNvDrmVideoSink *sink = GST_NVDRMVIDEOSINK (object); + + switch (prop_id) { + + case PROP_CONN_ID: + g_value_set_int (value, sink->conn_id); + break; + case PROP_PLANE_ID: + g_value_set_int (value, sink->plane_id); + break; + case PROP_SET_MODE: + g_value_set_boolean (value, sink->set_mode); + break; + case PROP_OFFSET_X: + g_value_set_int (value, sink->offset_x); + break; + case PROP_OFFSET_Y: + g_value_set_int (value, sink->offset_y); + break; + case PROP_COLOR_RANGE: + g_value_set_int (value, sink->color_range); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_nv_drm_video_sink_finalize (GObject * object) +{ + G_OBJECT_CLASS (gst_nv_drm_video_sink_parent_class)->finalize (object); +} + +static gboolean +nvdrmvideosink_init (GstPlugin * nvdrmvideosink) +{ + GST_DEBUG_CATEGORY_INIT (gst_nv_drm_video_sink_debug, "nvdrmvideosink", + 0, "Template nvdrmvideosink"); + + return gst_element_register (nvdrmvideosink, "nvdrmvideosink", + GST_RANK_NONE, GST_TYPE_NVDRMVIDEOSINK); +} + +/* + * TODO: Fix bug 200495960 that merges drm code into + * nvvideosink plugin code and then update license + */ +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + nvdrmvideosink, + "nvidia Drm Video Sink Component", + nvdrmvideosink_init, + "0.0.1", "Proprietary", "NvDrmVideoSink", "http://nvidia.com/") diff --git a/gst-nvdrmvideosink/gstnvdrmvideosink.h b/gst-nvdrmvideosink/gstnvdrmvideosink.h new file mode 100644 index 0000000..84c862b --- /dev/null +++ b/gst-nvdrmvideosink/gstnvdrmvideosink.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2017-2022, NVIDIA CORPORATION. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of NVIDIA CORPORATION nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __GST_NVDRMVIDEOSINK_H__ +#define __GST_NVDRMVIDEOSINK_H__ + +#include +#include "drmutil.h" + +#include "vt_switch.h" + +G_BEGIN_DECLS +/* #defines don't like whitespacey bits */ +#define GST_TYPE_NVDRMVIDEOSINK \ + (gst_nv_drm_video_sink_get_type()) +#define GST_NVDRMVIDEOSINK(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_NVDRMVIDEOSINK,GstNvDrmVideoSink)) +#define GST_NVDRMVIDEOSINK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_NVDRMVIDEOSINK,GstNvDrmVideoSinkClass)) +#define GST_IS_NVDRMVIDEOSINK(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_NVDRMVIDEOSINK)) +#define GST_IS_NVDRMVIDEOSINK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_NVDRMVIDEOSINK)) +typedef struct _GstNvDrmVideoSink GstNvDrmVideoSink; +typedef struct _GstNvDrmVideoSinkClass GstNvDrmVideoSinkClass; + +struct _GstNvDrmVideoSink +{ + GstVideoSink parent; + GstPad *sinkpad; + GstCaps *outcaps; + gint width; + gint height; + gint fps_n; + gint fps_d; + GstVideoFormat videoFormat; + struct drm_util_fb fb[2]; + struct drm_util_fb dumb; + gint conn_id; + gint crtc_id; + gint plane_id; + gint offset_x; + gint offset_y; + gint color_range; + guint buf_id[2]; + gint using_NVMM; + gint frame_count; + gint fd; + gint is_drc_on; + gboolean set_mode; + drmModeModeInfoPtr mode; + drmModeCrtcPtr default_crtcProp; + gint num_modes; + gint drm_format; + GstBuffer *last_buf; + drmModeConnector *conn_info; + gint drm_bo_handles[2][4]; + struct vt_info vtinfo; + gint do_vtswitch; + gboolean nvbuf_api_version_new; + gint is_nvidia_drm; + gint is_tegra_drm; +}; + +struct _GstNvDrmVideoSinkClass +{ + GstVideoSinkClass parent_class; +}; + +GType gst_nv_drm_video_sink_get_type (void); + +G_END_DECLS +#endif /* __GST_NVDRMVIDEOSINK_H__ */ diff --git a/gst-nvdrmvideosink/util/drmutil.c b/gst-nvdrmvideosink/util/drmutil.c new file mode 100644 index 0000000..8b28413 --- /dev/null +++ b/gst-nvdrmvideosink/util/drmutil.c @@ -0,0 +1,678 @@ +/* + * Copyright (c) 2018-2021, NVIDIA CORPORATION. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of NVIDIA CORPORATION nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "drmutil.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const struct util_format +{ + uint32_t drm_format; + int num_buffers; + struct + { + int w; // width divisor from overall fb_width (luma size) + int h; // height divisor from overall fb_height (luma size) + int bpp; + } buffers[3]; +} util_formats[] = { + // drm fourcc type #buffers w1 h1 bpp1 w2 h2 bpp2 w3 h3 bpp3 + // drm fourcc type #buffers w1 h1 bpp1 w2 h2 bpp2 w3 h3 bpp3 + { + DRM_FORMAT_ARGB8888, 1, { { + 1, 1, 32}, { + 0, 0, 0}, { + 0, 0, 0}}}, { + DRM_FORMAT_ABGR8888, 1, { { + 1, 1, 32}, { + 0, 0, 0}, { + 0, 0, 0}}}, { + DRM_FORMAT_NV12, 2, { { + 1, 1, 8}, { + 2, 2, 16}, { + 0, 0, 0}}}, { + DRM_FORMAT_YUV420, 3, { { + 1, 1, 8}, { + 2, 2, 8}, { + 2, 2, 8}}} , { + DRM_FORMAT_XRGB8888, 1, { { + 1, 1, 32}, { + 0, 0, 0}, { + 0, 0, 0}}} , { + DRM_FORMAT_UYVY, 1, { { + 1, 1, 16}, { + 0, 0, 0}, { + 0, 0, 0}}}, { + DRM_FORMAT_YUYV, 1, { { + 1, 1, 16}, { + 0, 0, 0}, { + 0, 0, 0}}} , { + DRM_FORMAT_XBGR8888, 1, { { + 1, 1, 32}, { + 0, 0, 0}, { + 0, 0, 0}}} , { + DRM_FORMAT_NV16, 2, { { + 1, 1, 8}, { + 2, 1, 16}, { + 0, 0, 0}}}, { + DRM_FORMAT_NV61, 2, { { + 1, 1, 8}, { + 2, 1, 16}, { + 0, 0, 0}}}, { + DRM_FORMAT_NV24, 2, { { + 1, 1, 8}, { + 1, 1, 16}, { + 0, 0, 0}}}, { + DRM_FORMAT_YVU420, 3, { { + 1, 1, 8}, { + 2, 2, 8}, { + 2, 2, 8}}}, { + DRM_FORMAT_YUV444, 3, { { + 1, 1, 8}, { + 1, 1, 8}, { + 1, 1, 8}}} +}; + +// This routine returns fail if no physical connector available +static int +drm_util_mode_get_resources(int fd, drmModeRes **res_info) +{ + if (res_info == NULL) + return -1; + + // Obtain DRM-KMS resources + *res_info = drmModeGetResources (fd); + if (*res_info == NULL) { + return -1; + } else if ((*res_info)->count_connectors < 1) { + drmModeFreeResources (*res_info); + *res_info = NULL; + return -1; + } + + return 0; +} + +static int +drm_util_find_device(drmModeRes **res_info) +{ + DIR *dir; + struct dirent *dent; + int fd = -1; + + dir = opendir("/dev/dri"); + if (!dir) + goto no_dri_device; + + while ((dent = readdir(dir))) { + char path[265]; + fd = -1; + + if (strncmp(dent->d_name, "card", 4)) + continue; + + snprintf(path, sizeof(path), "/dev/dri/%s", dent->d_name); + + fd = open(path, O_RDWR|O_CLOEXEC); + if (fd < 0) + continue; + + if (drm_util_mode_get_resources(fd, res_info) < 0) { + close(fd); + continue; + } + + closedir(dir); + return fd; + } + + closedir(dir); + +no_dri_device: + fd = drmOpen("drm-nvdc", NULL); + if (fd >= 0) { + if (drm_util_mode_get_resources(fd, res_info) < 0) { + close(fd); + return -1; + } + } + return fd; +} + +int +drm_util_init (int *fd, drmModeConnector ** conn_info_s, int *conn_id_s, + int *crtc_id_s, int *plane_id_s, struct vt_info *vtinfo, gint do_vtswitch, + gint *is_nvidia_drm, gint *is_tegra_drm) +{ + int conn_index = *conn_id_s; + int plane_index = *plane_id_s; + int crtc_index = *crtc_id_s; + uint32_t conn_id = 0; + uint32_t enc_id; + uint32_t crtc_id; + drmModeRes *res_info = NULL; + drmModePlaneRes *plane_res_info = NULL; + drmModeEncoder *enc_info = NULL; + drmModeCrtc *crtc_info = NULL; + drmModePlane *plane_info = NULL; + drmVersion *version = NULL; + int is; + int ret = 0; + + if (do_vtswitch) { + if (!acquire_vt(vtinfo)) { + g_print ("Failed to acquire vt\n"); + return 0; + } + } + + // Open the DRM device. + *fd = drm_util_find_device(&res_info); + if (*fd < 0) { + g_print ("Could not open DRM failed \n"); + return 0; + } + + version = drmGetVersion(*fd); + if (version == NULL) { + g_print("Failed to get drm version\n"); + drmClose (*fd); + goto free_resources; + } + *is_nvidia_drm = 0; + if (!strcmp(version->name, "nvidia-drm")) { + *is_nvidia_drm = 1; + } + *is_tegra_drm = 0; + if (!strcmp(version->name, "tegra")) { + *is_tegra_drm = 1; + } + drmFreeVersion(version); + + if (drmSetClientCap (*fd, DRM_CLIENT_CAP_ATOMIC, 1)) { + g_print ("Failed to set atomic cap \n"); + goto free_resources; + } + + if (drmSetClientCap (*fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1)) { + g_print ("Failed to set universal planes \n"); + goto free_resources; + } + + // If user has not mentioned any conn_id + if (conn_index == INT_MAX) { + // Find a valid/connected connector + for (conn_index = 0; conn_index < res_info->count_connectors ; conn_index++) { + conn_id = res_info->connectors[conn_index]; + *conn_info_s = drmModeGetConnector(*fd, conn_id); + if ((*conn_info_s) && (*conn_info_s)->connection == DRM_MODE_CONNECTED) { + *conn_id_s = conn_id; + break; + } + drmModeFreeConnector(*conn_info_s); + } + } else if (conn_index < res_info->count_connectors && conn_index >=0){ + conn_id = res_info->connectors[conn_index]; + *conn_id_s = conn_id; + *conn_info_s = drmModeGetConnector (*fd, conn_id); + } + // Check bound + if ((conn_index >= res_info->count_connectors) || (conn_index < 0)) { + g_print ("Invalid connector id \n"); + goto free_resources; + } + + if (!(*conn_info_s)) { + g_print ("Unable to obtain info for connector id(%d) \n", conn_id); + goto free_resources; + } + // Set Encoder Info + enc_id = (*conn_info_s)->encoder_id; + enc_info = drmModeGetEncoder (*fd, enc_id); + + if (!enc_info) { + // If connector does not have a connected encoder use first one + enc_id = (*conn_info_s)->encoders[0]; + enc_info = drmModeGetEncoder (*fd, enc_id); + + if (!enc_info) { + g_print ("Unable to find suitable encoder \n"); + goto free_resources; + } + } + // Set CRTC info + crtc_id = enc_info->crtc_id; + crtc_info = drmModeGetCrtc (*fd, crtc_id); + + // If the encoder has no connected CRTC, use the first one supported + if (!crtc_info && enc_info->possible_crtcs) { + crtc_id = res_info->crtcs[ffs (enc_info->possible_crtcs) - 1]; + crtc_info = drmModeGetCrtc (*fd, crtc_id); + } + + *crtc_id_s = crtc_id; + drmModeFreeEncoder (enc_info); + + if (!crtc_info) { + g_print ("Unable to find usable crtc for encoder \n"); + goto free_resources; + } + // Identify the index of our chosen crtc from the resource list. + // Index is needed to match planes with crtcs + crtc_index = -1; + for (is = 0; is < res_info->count_crtcs; is++) { + if (res_info->crtcs[is] == crtc_id) { + crtc_index = is; + break; + } + } + if (crtc_index == -1) { + printf ("Unable to locate crtc_id=%d in resource list\n", crtc_id); + goto free_resources; + } + // Set Plane Info + plane_res_info = drmModeGetPlaneResources (*fd); + int plane_count = plane_res_info->count_planes; + if (!plane_res_info) { + g_print ("Unable to get plane resource info \n"); + goto free_resources; + } + // If user has not mentioned any plane_id + if (plane_index == INT_MAX) { + plane_index = 0; + } + // Check bound + if ((plane_index >= plane_count) || (plane_index < 0)) { + g_print ("Invalid plane id \n"); + goto free_resources; + } + + int i; + for (i = 0; i < plane_count; i++) { + int plane_id = plane_res_info->planes[i]; + plane_info = drmModeGetPlane (*fd, plane_id); + if (!plane_info) { + printf ("Unable to get plane info\n"); + return 0; + } + if (plane_info->possible_crtcs & (1 << crtc_index)) { + if (plane_index == 0) { + *plane_id_s = plane_id; + break; + } else { + plane_index--; + } + } + drmModeFreePlane (plane_info); + } + + if (plane_index != 0) { + goto free_resources; + } + + //drm_util_init success + ret = 1; + +// Free the resource information structures +free_resources: + if (plane_info) + drmModeFreePlane (plane_info); + + if (plane_res_info) + drmModeFreePlaneResources (plane_res_info); + + if (crtc_info) + drmModeFreeCrtc (crtc_info); + + if (res_info) + drmModeFreeResources (res_info); + + if (!ret){ + if (*conn_info_s) { + drmModeFreeConnector (*conn_info_s); + } + drmClose (*fd); + } + + return ret; +} + +static int +get_format_info (uint32_t drm_format, struct util_format *uf) +{ + int i; + int fsize = sizeof (util_formats) / sizeof (util_formats[0]); + for (i = 0; i < fsize; i++) { + if (util_formats[i].drm_format == drm_format) { + *uf = util_formats[i]; + return 1; + } + } + return 0; +} + +uint8_t * +drm_util_mmap_dumb_bo (int fd, __u32 handle, __u64 size) +{ + struct drm_mode_map_dumb mreq; + uint8_t *map = NULL; + drmVersion *version; + + /* prepare buffer for memory mapping */ + memset (&mreq, 0, sizeof (mreq)); + mreq.handle = handle; + if (drmIoctl (fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq) != 0) { + g_print ("cannot map dumb buffer\n"); + return NULL; + } + + version = drmGetVersion(fd); + if (version == NULL) { + g_print("Failed to get drm version\n"); + return NULL; + } + + if (!strcmp(version->name, "nvidia-drm") || + !strcmp(version->name, "tegra") || + !strcmp(version->name, "tegra-udrm")) { + map = (uint8_t*)mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, + mreq.offset); + + if (map == MAP_FAILED) { + g_print("cannot mmap dumb buffer\n"); + drmFreeVersion(version); + return NULL; + } + } else { + map = (uint8_t *) (mreq.offset); + } + + drmFreeVersion(version); + + return map; +} + +// Create a buffer object of the requested size. +// Return the bo handle and the mapping +int +drm_util_create_dumb_bo (int fd, int width, int height, + int bpp, struct drm_util_bo *util_bo) +{ + struct drm_mode_create_dumb creq; + struct drm_mode_destroy_dumb dreq; + int ret; + uint8_t *map = NULL; + + /* create dumb buffer */ + memset (&creq, 0, sizeof (creq)); + creq.width = width; + creq.height = height; + creq.bpp = bpp; + ret = drmIoctl (fd, DRM_IOCTL_MODE_CREATE_DUMB, &creq); + if (ret < 0) { + g_print ("cannot create dumb buffer\n"); + return 0; + } + + map = drm_util_mmap_dumb_bo (fd, creq.handle, creq.size); + if (map == NULL) { + goto err_destroy; + } + + /* clear the buffer object */ + memset (map, 0x00, creq.size); + util_bo->bo_handle = creq.handle; + util_bo->width = width; + util_bo->height = height; + util_bo->pitch = creq.pitch; + util_bo->data = map; + util_bo->size = creq.size; + + return 1; + +err_destroy: + memset (&dreq, 0, sizeof (dreq)); + dreq.handle = creq.handle; + drmIoctl (fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq); + + return 0; +} + +int +drm_util_fill_data (struct drm_util_fb *util_fb, uint8_t * p, + uint32_t sizeInput) +{ + int i, j; + int w = util_fb->width; + int h = util_fb->height; + int fmt = util_fb->format; + + struct util_format uf; + if (!get_format_info (util_fb->format, &uf)) { + GST_ERROR ("DRM test helper library can't draw in a FB of type %x\n", + util_fb->format); + return 1; + } + + switch (fmt) { + case DRM_FORMAT_ABGR8888: + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_XBGR8888: + memcpy (util_fb->bo[0].data, p, sizeInput); + break; + case DRM_FORMAT_NV16: + case DRM_FORMAT_NV61: + case DRM_FORMAT_NV24: + case DRM_FORMAT_NV12: + for (j = 0; j < h; j++) { + for (i = 0; i < w; i++) { + uint32_t off_y = util_fb->bo[0].pitch * j + i; + util_fb->bo[0].data[off_y] = *p++; + } + } + for (j = 0; j < (h / uf.buffers[1].h); j++) { + for (i = 0; (i < w / uf.buffers[1].w); i++) { + uint32_t off_u = + (util_fb->bo[1].pitch * j) + (i * uf.buffers[1].bpp / 8); + util_fb->bo[1].data[off_u] = *p++; + util_fb->bo[1].data[off_u + 1] = *p++; + } + } + break; + case DRM_FORMAT_YVU420: + case DRM_FORMAT_YUV420: + case DRM_FORMAT_YUV444: + for (j = 0; j < h; j++) { + for (i = 0; i < w; i++) { + uint32_t off_y = util_fb->bo[0].pitch * j + i; + util_fb->bo[0].data[off_y] = *p++; + } + } + for (j = 0; j < (h / uf.buffers[1].h); j++) { + for (i = 0; i < (w / uf.buffers[1].w); i++) { + uint32_t off_y = util_fb->bo[1].pitch * j + i; + util_fb->bo[1].data[off_y] = *p++; + } + } + for (j = 0; (j < (h / uf.buffers[1].h)); j++) { + for (i = 0; (i < (w / uf.buffers[1].w)); i++) { + uint32_t off_y = util_fb->bo[2].pitch * j + i; + util_fb->bo[2].data[off_y] = *p++; + } + } + break; + case DRM_FORMAT_UYVY: + case DRM_FORMAT_YUYV: + for (j = 0; j < h; j++) { + for (i = 0; i < w; i++) { + uint32_t off_y = util_fb->bo[0].pitch * j + i * 2; + util_fb->bo[0].data[off_y + 0] = *p++; + util_fb->bo[0].data[off_y + 1] = *p++; + } + } + break; + default: + g_print ("Fill format not defined \n"); + return 1; + } + + return 0; +} + +int +gem_set_params (int fd, uint32_t nvhandle, uint32_t nvblocksize) +{ + struct drm_tegra_gem_set_tiling args; + memset (&args, 0, sizeof (args)); + args.handle = nvhandle; + args.mode = DRM_TEGRA_GEM_TILING_MODE_BLOCK; + args.value = nvblocksize; + + int ret = drmIoctl (fd, DRM_IOCTL_TEGRA_GEM_SET_TILING, &args); + if (ret < 0) { + g_print ("Failed to set tiling parameters \n"); + return -1; + } + return 1; +} + + +int +drm_util_destroy_dumb_bo (int fd, struct drm_util_bo *util_bo) +{ + struct drm_mode_destroy_dumb dreq; + + munmap (util_bo->data, util_bo->size); + + memset (&dreq, 0, sizeof (dreq)); + dreq.handle = util_bo->bo_handle; + drmIoctl (fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq); + + return 1; +} + +// Destroy an FB and its BOs +int +drm_util_destroy_dumb_fb (int fd, struct drm_util_fb *util_fb) +{ + int i = 0; + for (i = 0; i < util_fb->num_buffers; i++) { + drm_util_destroy_dumb_bo (fd, &util_fb->bo[i]); + } + util_fb->num_buffers = 0; + + drmModeRmFB (fd, util_fb->fb_id); + + return 1; +} + + +// Create an FB of the requested size. Return the fb_id and the mapping +int +drm_util_create_dumb_fb (int fd, int width, int height, + int drm_format, struct drm_util_fb *util_fb) +{ + int buf_count; + int ret = 1; + int i = 0; + struct util_format uf; + if (!get_format_info (drm_format, &uf)) { + g_print ("DRM test helper library can't make a FB of type %d\n", + drm_format); + return 0; + } + buf_count = uf.num_buffers; + uint32_t buf_id; + uint32_t bo_handles[4] = { 0 }; + uint32_t pitches[4] = { 0 }; + uint32_t offsets[4] = { 0 }; + + util_fb->num_buffers = 0; + + /* create dumb buffers */ + for (i = 0; i < buf_count; i++) { + struct drm_util_bo *util_bo = &(util_fb->bo[i]); + ret = drm_util_create_dumb_bo (fd, width / uf.buffers[i].w, + height / uf.buffers[i].h, uf.buffers[i].bpp, util_bo); + if (ret <= 0) { + g_print ("cannot create dumb buffer\n"); + goto err_destroy; + } + + bo_handles[i] = util_fb->bo[i].bo_handle; + pitches[i] = util_fb->bo[i].pitch; + offsets[i] = 0; + util_fb->num_buffers++; + + } + + /* create framebuffer object for the dumb-buffer */ + ret = drmModeAddFB2 (fd, width, height, drm_format, bo_handles, + pitches, offsets, &buf_id, 0); + if (ret) { + g_print ("cannot create framebuffer\n"); + goto err_destroy; + } + + util_fb->fb_id = buf_id; + util_fb->width = width; + util_fb->height = height; + util_fb->format = drm_format; + + return 1; + +err_destroy: + drm_util_destroy_dumb_fb (fd, util_fb); + return 0; + +} + +// Close GEM buffers +int +drm_util_close_gem_bo (int fd, uint32_t bo_handle) +{ + struct drm_gem_close gemCloseArgs; + + memset (&gemCloseArgs, 0, sizeof (gemCloseArgs)); + gemCloseArgs.handle = bo_handle; + int ret = drmIoctl (fd, DRM_IOCTL_GEM_CLOSE, &gemCloseArgs); + if (ret < 0) { + return 0; + } + return 1; +} diff --git a/gst-nvdrmvideosink/util/drmutil.h b/gst-nvdrmvideosink/util/drmutil.h new file mode 100644 index 0000000..cb83c81 --- /dev/null +++ b/gst-nvdrmvideosink/util/drmutil.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2017-2020, NVIDIA CORPORATION. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of NVIDIA CORPORATION nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DRMUTIL_H +#define _DRMUTIL_H + +#include "vt_switch.h" + +#include "xf86drm.h" +#include "xf86drmMode.h" + +struct drm_util_bo +{ + uint32_t bo_handle; + int width; + int height; + int pitch; + uint8_t *data; + size_t size; +}; + +struct drm_util_fb +{ + uint32_t fb_id; + int width; + int height; + int format; + struct drm_util_bo bo[4]; + int num_buffers; +}; + +// Fill drm dumb buffer with a test pattern +int drm_util_fill_data (struct drm_util_fb *util_fb, uint8_t * p, + uint32_t sizeInput); + +int drm_util_init (int *fd, drmModeConnector **conn_info, int *conn_id, int *crtc_id, + int *plane_id, struct vt_info *vtinfo, int do_vtswitch, + int *is_nvidia_drm, int *is_tegra_drm); + +// Returns a DRM buffer_object handle allocated of size (w,h) +int drm_util_create_dumb_bo (int fd, int w, int h, + int bpp, struct drm_util_bo *util_bo); + +uint8_t *drm_util_mmap_dumb_bo (int fd, __u32 handle, __u64 size); + +// Set gem buffer parameters +int gem_set_params (int fd, uint32_t nvhandle, uint32_t nvblocksize); + +// Returns a DRM framebuffer ID allocated of size (w,h) +int drm_util_create_dumb_fb (int fd, int w, int h, + int drm_format, struct drm_util_fb *util_fb); + +int count_format_types(void); + +// Close GEM buffers +int drm_util_close_gem_bo (int fd, uint32_t bo_handle); + +// Destroy an FB and its BOs +int drm_util_destroy_dumb_bo (int fd, struct drm_util_bo *util_bo); +int drm_util_destroy_dumb_fb (int fd, struct drm_util_fb *util_fb); +#endif diff --git a/gst-nvdrmvideosink/util/vt_switch.c b/gst-nvdrmvideosink/util/vt_switch.c new file mode 100644 index 0000000..9eaffd5 --- /dev/null +++ b/gst-nvdrmvideosink/util/vt_switch.c @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of NVIDIA CORPORATION nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vt_switch.h" + +/* + * release_vt - Sets the tty back to TEXT mode. + */ +void release_vt(struct vt_info *info) +{ + int ret; + int kd_mode; + + if (info->console_fd < 0) { + return; + } + + ret = ioctl(info->console_fd, KDSETMODE, KD_TEXT); + if (ret < 0) { + printf("KDSETMODE KD_TEXT failed, err=%s\n", strerror(errno)); + goto fail; + } + + ret = ioctl(info->console_fd, KDGETMODE, &kd_mode); + if (ret < 0) { + printf("KDGETMODE failed, err=%s\n", strerror(errno)); + goto fail; + } + + if (info->active_vt >= 0) { + ret = ioctl(info->console_fd, VT_ACTIVATE, info->active_vt); + if (ret < 0) { + printf("VT_ACTIVATE failed, err=%s\n", strerror(errno)); + goto fail; + } + + ret = ioctl(info->console_fd, VT_WAITACTIVE, info->active_vt); + if (ret < 0) { + printf("VT_WAITACTIVE failed, err= %s\n", strerror(errno)); + goto fail; + } + } + +fail: + close(info->console_fd); + + info->console_fd = -1; + info->active_vt = -1; +} + +/* + * acquire_vt - Sets the tty to GRAPHICAL mode. + */ +int acquire_vt(struct vt_info *info) +{ + int i, ret, fd, vtno, kd_mode; + struct vt_stat vts; + const char *vcs[] = { "/dev/vc/%d", "/dev/tty%d", NULL }; + static char vtname[11]; + + fd = open("/dev/tty0", O_WRONLY); + if (fd < 0) { + printf("Can't open /dev/tty0 err=%s\n", strerror(errno)); + return 0; + } + + ret = ioctl(fd, VT_OPENQRY, &vtno); + if (ret < 0) { + printf("VT_OPENQRY failed, err=%s\n", strerror(errno)); + close(fd); + return 0; + } + + if (vtno == -1) { + printf("Can't find free VT\n"); + close(fd); + return 0; + } + + printf("Using VT number %d\n", vtno); + close(fd); + + i = 0; + while (vcs[i] != NULL) { + snprintf(vtname, sizeof(vtname), vcs[i], vtno); + info->console_fd = open(vtname, O_RDWR | O_NDELAY, 0); + if (info->console_fd >= 0) { + break; + } + i++; + } + + if (info->console_fd < 0) { + printf("Can't open virtual console %d\n", vtno); + return 0; + } + + + ret = ioctl(info->console_fd, KDGETMODE, &kd_mode); + if (ret < 0) { + printf("KDGETMODE failed, err=%s\n", strerror(errno)); + goto fail; + } + + if (kd_mode != KD_TEXT) { + printf("%s is already in graphics mode, " + "seems like some display server running\n", vtname); + } + + ret = ioctl(info->console_fd, VT_ACTIVATE, vtno); + if (ret < 0) { + printf("VT_ACTIVATE failed, err=%s\n", strerror(errno)); + goto fail; + } + + ret = ioctl(info->console_fd, VT_WAITACTIVE, vtno); + if (ret < 0) { + printf("VT_WAITACTIVE failed, err=%s\n", strerror(errno)); + goto fail; + } + + ret = ioctl(info->console_fd, VT_GETSTATE, &vts); + if (ret < 0) { + printf("VT_GETSTATE failed, err=%s\n", strerror(errno)); + goto fail; + } else { + info->active_vt = vts.v_active; + } + + ret = ioctl(info->console_fd, KDSETMODE, KD_GRAPHICS); + if (ret < 0) { + printf("KDSETMODE KD_GRAPHICS failed, err=%s\n", strerror(errno)); + goto fail; + } + + ret = ioctl(info->console_fd, KDGETMODE, &kd_mode); + if (ret < 0) { + printf("KDGETMODE failed, err=%s\n", strerror(errno)); + goto fail; + } + + if (kd_mode != KD_TEXT) { + printf("%s is in graphics mode\n", vtname); + } + + return 1; + +fail: + close (info->console_fd); + return 0; +} diff --git a/gst-nvdrmvideosink/util/vt_switch.h b/gst-nvdrmvideosink/util/vt_switch.h new file mode 100644 index 0000000..c7ecca7 --- /dev/null +++ b/gst-nvdrmvideosink/util/vt_switch.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of NVIDIA CORPORATION nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _VT_SWITCH_H +#define _VT_SWITCH_H + +struct vt_info { + int console_fd; + int active_vt; +}; + +int acquire_vt(struct vt_info *info); + +void release_vt(struct vt_info *info); + +#endif diff --git a/push_info.txt b/push_info.txt new file mode 100644 index 0000000..051d5f1 --- /dev/null +++ b/push_info.txt @@ -0,0 +1 @@ +jetson_36.3