Files
libgstnvdrmvideosink/gst-nvdrmvideosink/gstnvdrmvideosink.c
svcmobrel-release 1dceed9357 Updating prebuilts and/or headers
464f6823a2241ec38d94c786e23d13dd4b4e2a14 - gst-nvdrmvideosink/Makefile
5335db7375d2cee2cf0c02c68879205293dc63e1 - gst-nvdrmvideosink/gstnvdrmvideosink.c
444ba0e5976368618bd65182f9618af35e0b7946 - gst-nvdrmvideosink/LICENSE.libgstnvdrmvideosink
15e77cc11e0be2e813a2a51eaa4256b1026dd16b - gst-nvdrmvideosink/gstnvdrmvideosink.h
c318d575f2c7cd3e805b4283f48da5547b152fd6 - gst-nvdrmvideosink/util/vt_switch.c
31762975c758c9a6c0a0f2a811d6850440f347b6 - gst-nvdrmvideosink/util/vt_switch.h
ffd00e6dfee167cbf1a33f34d4b2c94269bd21b3 - gst-nvdrmvideosink/util/drmutil.c
ef0f0a9b3cfbf3d5f2359e9185a75d258201e20a - gst-nvdrmvideosink/util/drmutil.h

Change-Id: I63947f3c369118cc2477b18542e1ca1aab294474
2024-09-06 00:00:25 -07:00

1256 lines
36 KiB
C

/*
* 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 <errno.h>
#include <unistd.h>
#include <dlfcn.h>
#include <gst/gst.h>
#include <gst/video/gstvideosink.h>
#include <gst/video/video.h>
#include "gstnvdrmvideosink.h"
#include <stdio.h>
#include "util/drmutil.h"
#include <drm_fourcc.h>
#include <stdlib.h>
#include <string.h>
#include <nvbufsurface.h>
#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 <amunje@nvidia.com>");
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/")