blob: 66afd0a25c9ef14f0ed23064ef0f14ca2b4311cb [file] [log] [blame]
/* GStreamer
* Copyright (C) 2022 amlogic
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Suite 500,
* Boston, MA 02110-1335, USA.
*/
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdbool.h>
#include <sys/utsname.h>
#include "resourcemanage.h"
#include "meson_drm.h"
#include <errno.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <drm/drm_fourcc.h>
#include "ge2drotation.h"
//#include <gst/gstdrmbufferpool.h>
#include <gst/allocators/gstdmabuf.h>
#define DUMP_ROTATION_DATA 0
static int mdrmFd = -1;
static void destroy_gem_buffer(int drmFd, GemBuffer *gemBuf)
{
int rc;
struct drm_gem_close gclose;
for (int i= 0; i < gemBuf->planeCount; ++i)
{
if (gemBuf->fd[i] >= 0)
{
close(gemBuf->fd[i]);
gemBuf->fd[i]= -1;
}
if (gemBuf->handle[i] > 0)
{
memset(&gclose, 0, sizeof(gclose));
gclose.handle= gemBuf->handle[i];
rc= ioctl(drmFd, DRM_IOCTL_GEM_CLOSE, &gclose);
if (rc < 0)
{
GST_ERROR("Failed to release gem buffer handle %d: DRM_IOCTL_MODE_DESTROY_DUMB rc %d errno %d",
gemBuf->handle[i], rc, errno );
}
gemBuf->handle[i]= 0;
}
}
}
void rotation_buffer_teardown(ROTBuffer *ROTbuf, int numBuffer)
{
if (NULL == ROTbuf || 0 == numBuffer)
{
return;
}
for (int i= 0; i < numBuffer; ++i)
{
destroy_gem_buffer(mdrmFd, &ROTbuf[i].gemBuf);
}
free(ROTbuf);
ROTbuf = NULL;
if (mdrmFd >= 0)
{
close(mdrmFd);
mdrmFd= -1;
}
}
#define DEFAULT_DRM_NAME "/dev/dri/renderD128"
static bool creat_gem_buffer(int drmFd, GemBuffer *gemBuf)
{
bool result= false;
int rc;
struct drm_meson_gem_create gc;
for (int i= 0; i < gemBuf->planeCount; ++i)
{
gemBuf->fd[i]= -1;
memset(&gc, 0, sizeof(gc));
// NV12M
{
gc.flags= MESON_USE_VIDEO_PLANE;
if (i == 0)
{
gc.size= gemBuf->width*gemBuf->height;
}
else
{
gc.size= gemBuf->width*gemBuf->height/2;
}
gemBuf->stride[i]= gemBuf->width;
}
gemBuf->size[i]= gc.size;
rc= ioctl(drmFd, DRM_IOCTL_MESON_GEM_CREATE, &gc);
if (rc < 0)
{
GST_ERROR("Failed to create gem buffer: plane %d DRM_IOCTL_MESON_GEM_CREATE rc %d", i, rc);
goto exit;
}
gemBuf->handle[i]= gc.handle;
gemBuf->offset[i]= 0;
rc= drmPrimeHandleToFD(drmFd, gemBuf->handle[i], DRM_CLOEXEC | DRM_RDWR, &gemBuf->fd[i]);
if (rc < 0)
{
GST_ERROR("Failed to get fd for gem buf plane %d: handle %d drmPrimeHandleToFD rc %d",
i, gemBuf->handle[i], rc );
goto exit;
}
GST_DEBUG("create gem buf plane %d size (%dx%d) %u bytes stride %d offset %d handle %d fd %d",
i, gemBuf->width, gemBuf->height, gemBuf->size[i],
gemBuf->stride[i], gemBuf->offset[i], gemBuf->handle[i], gemBuf->fd[i] );
}
result= true;
exit:
return result;
}
ROTBuffer* rotation_buffer_setup(int numPlanes, int width, int height, int numBuffer)
{
bool result= false;
if (0 == numBuffer)
{
return NULL;
}
ROTBuffer* ROTbuf = (ROTBuffer*)calloc(numBuffer, sizeof(ROTBuffer));
if (!ROTbuf)
{
GST_ERROR("no memory for GemBuffer" );
return NULL;
}
char *drmName = DEFAULT_DRM_NAME;
mdrmFd= open(drmName, O_RDWR|O_CLOEXEC);
if (mdrmFd < 0)
{
GST_ERROR("Failed to open drm render node: %d", errno);
goto exit;
}
for (int i= 0; i < numBuffer; ++i)
{
ROTbuf[i].used = FALSE;
ROTbuf[i].gemBuf.planeCount= numPlanes;
ROTbuf[i].gemBuf.width= width;
ROTbuf[i].gemBuf.height= height;
for (int j= 0; j < MAX_PLANES; ++j)
{
ROTbuf[i].gemBuf.fd[j]= -1;
}
if (!creat_gem_buffer(mdrmFd, &ROTbuf[i].gemBuf))
{
GST_ERROR("Failed to allocate gem buffer");
goto exit;
}
}
result= true;
exit:
if (!result)
{
rotation_buffer_teardown(ROTbuf, numBuffer);
ROTbuf = NULL;
}
return ROTbuf;
}
gboolean rotation_init(aml_ge2d_t* pAmlge2d, aml_ge2d_info_t **pge2dinfo)
{
int ret = 0;
if (NULL == pAmlge2d)
{
return FALSE;
}
*pge2dinfo = &(pAmlge2d->ge2dinfo);
memset(pAmlge2d, 0, sizeof(aml_ge2d_t));
/* ge2d init */
ret = aml_ge2d_init(pAmlge2d);
if (ret < 0)
{
GST_ERROR("ge2d init failed");
return FALSE;
}
return TRUE;
}
gboolean rotation_exit(aml_ge2d_t* pAmlge2d)
{
if (NULL == pAmlge2d)
{
return FALSE;
}
//ge2d release exit
aml_ge2d_exit(pAmlge2d);
return TRUE;
}
#if DUMP_ROTATION_DATA
static int aml_write_file(int shared_fd, const char* file_name, int write_bytes)
{
int fd = -1;
int write_num = 0;
char *vaddr = NULL;
if (shared_fd < 0 || !file_name || !write_bytes) {
printf("wrong params, read failed");
return -1;
}
vaddr = (char*)mmap(NULL, write_bytes, PROT_READ | PROT_WRITE, MAP_SHARED, shared_fd, 0);
if (!vaddr) {
printf("%s,%d,mmap failed,Not enough memory\n",__func__, __LINE__);
return -1;
}
fd = open(file_name, O_RDWR | O_CREAT | O_APPEND, 0660);
if (fd < 0) {
printf("write file:%s open error\n", file_name);
return -1;
}
write_num = write(fd, vaddr, write_bytes);
if (write_num <= 0) {
printf("write file write_num=%d error\n", write_num);
}
close(fd);
munmap(vaddr, write_bytes);
return 0;
}
#endif
gboolean get_rotation_size(int src_w, int src_h, int* dst_w, int* dst_h)
{
if (NULL == dst_w || NULL == dst_h)
{
return FALSE;
}
int dst_width = src_w;
int dst_height = src_h;
// reduce to 720p
if (dst_width > 1280 || dst_height > 720)
{
int ratio = dst_width * 100 / dst_height;
if (ratio > 100)
{
dst_width = 1280;
dst_height = dst_width *100 / ratio;
}
else
{
dst_height = 720;
dst_width = dst_height * ratio / 100;
}
GST_DEBUG("ratio:%d, w:%d, h:%d\n", ratio, dst_width, dst_height);
}
*dst_w = dst_width/32*32;
*dst_h = dst_height;
return TRUE;
}
GstFlowReturn rotation_transform(aml_ge2d_info_t *pge2dinfo, GstBuffer *inbuf, ROTBuffer *ROTbuf, ROTATION_DEGREE ROTDegree)
{
GstFlowReturn ret = GST_FLOW_ERROR;
GstMemory *mem;
int i;
int plane = 2;
GstVideoMeta *meta_data = NULL;
if (NULL == pge2dinfo || NULL == inbuf || NULL == ROTbuf)
{
return GST_FLOW_ERROR;
}
plane = gst_buffer_n_memory(inbuf);
for (i = 0; i < plane; i++)
{
mem = gst_buffer_peek_memory(inbuf, i);
g_return_val_if_fail(gst_is_drm_memory(mem), ret);
pge2dinfo->src_info[0].shared_fd[i] = gst_fd_memory_get_fd(mem);
pge2dinfo->dst_info.shared_fd[i] = ROTbuf->gemBuf.fd[i];
}
meta_data = gst_buffer_get_video_meta(inbuf);
// pge2dinfo->mem_sec = plugin->secure ? 1 : 0;
int src_width = meta_data->width;
int src_height = meta_data->height;
int dst_width = ROTbuf->gemBuf.width;
int dst_height = ROTbuf->gemBuf.height;
// Align to 32-bit
dst_width = dst_width/32*32;
pge2dinfo->src_info[0].memtype = GE2D_CANVAS_ALLOC;
pge2dinfo->src_info[0].mem_alloc_type = AML_GE2D_MEM_DMABUF;
pge2dinfo->src_info[0].plane_number = plane; /* When allocating memory, it is a continuous block or separate multiple blocks */
pge2dinfo->src_info[0].canvas_w = src_width; /* input width */
pge2dinfo->src_info[0].canvas_h = src_height; /* input height */
pge2dinfo->src_info[0].format = PIXEL_FORMAT_YCbCr_420_SP_NV12;
pge2dinfo->src_info[0].rect.x = 0; /* input process area x */
pge2dinfo->src_info[0].rect.y = 0; /* input process area y */
pge2dinfo->src_info[0].rect.w = src_width; /* input process area w */
pge2dinfo->src_info[0].rect.h = src_height; /* input process area h */
pge2dinfo->src_info[0].plane_alpha = 0xFF; /* global plane alpha*/
pge2dinfo->dst_info.memtype = GE2D_CANVAS_ALLOC;
pge2dinfo->dst_info.mem_alloc_type = AML_GE2D_MEM_DMABUF;
pge2dinfo->dst_info.plane_number = plane; /* When allocating memory, it is a continuous block or separate multiple blocks */
pge2dinfo->dst_info.canvas_w = dst_width; /* output width */
pge2dinfo->dst_info.canvas_h = dst_height; /* output height */
pge2dinfo->dst_info.format = PIXEL_FORMAT_YCbCr_420_SP_NV12;
pge2dinfo->dst_info.rect.x = 0; /* output process area x */
pge2dinfo->dst_info.rect.y = 0; /* output process area y */
pge2dinfo->dst_info.rect.w = dst_width; /* output process area w */
pge2dinfo->dst_info.rect.h = dst_height; /* output process area h */
pge2dinfo->dst_info.plane_alpha = 0xFF; /* global plane alpha*/
pge2dinfo->dst_info.rotation = ROTDegree;
pge2dinfo->ge2d_op = AML_GE2D_STRETCHBLIT;
if (pge2dinfo->src_info[0].format == -1 || pge2dinfo->dst_info.format == -1) {
GST_ERROR("ge2d not support format src_info[0].format %d, dst_info.format %d",pge2dinfo->src_info[0].format, pge2dinfo->dst_info.format);
goto beach;
}
#if DUMP_ROTATION_DATA
// DEBUG: dump src data
aml_write_file(pge2dinfo->src_info[0].shared_fd[0], "/tmp/input.yuv", src_width*src_height);
aml_write_file(pge2dinfo->src_info[0].shared_fd[1], "/tmp/input.yuv", src_width*src_height/2);
#endif
GST_DEBUG("pge2dinfo: in_rect_w %d in_rect_h %d canvas_w %d canvas_h %d" , \
pge2dinfo->src_info[0].rect.w, pge2dinfo->src_info[0].rect.h, \
pge2dinfo->src_info[0].canvas_w,pge2dinfo->src_info[0].canvas_h);
ret = aml_ge2d_process(pge2dinfo);
if (ret < 0) {
GST_ERROR("ge2d process failed");
goto beach;
}
GST_DEBUG("pge2dinfo: out_rect_w %d out_rect_h %d canvas_w %d canvas_h %d" , \
pge2dinfo->dst_info.rect.w, pge2dinfo->dst_info.rect.h,\
pge2dinfo->dst_info.canvas_w,pge2dinfo->dst_info.canvas_h);
#if DUMP_ROTATION_DATA
// DEBUG: dump dst data
aml_write_file(pge2dinfo->dst_info.shared_fd[0], "/tmp/output.yuv", dst_width*dst_height);
aml_write_file(pge2dinfo->dst_info.shared_fd[1], "/tmp/output.yuv", dst_width*dst_height/2);
#endif
ret = GST_FLOW_OK;
beach:
return ret;
}