| /* 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; |
| } |