/* * Copyright (C) 2024 Amlogic Corporation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "vsc_fuse.h" #include #include #include #include #include "xf86drmMode.h" #include "xf86drm.h" #include "drm_fourcc.h" #define NAME_LEN 32 #define FUNC_NAME_LEN 100 #define BUFFER_MAX_NUMBER 3 #define VT_TIME_STAMP 37510560 #define STR_HELPER(x) #x #define SET_PARAMETER(name, v4l2id) void set_##name(int video_fd, char *par) \ { \ struct v4l2_control ctl = { 0 }; \ \ ctl.id = v4l2id; \ get_int_for_flag(par, STR_HELPER(name), (int *)&ctl.value); \ int rc = ioctl(video_fd, VIDIOC_S_CTRL, &ctl); \ if (rc < 0) { \ fprintf(stderr, "set %s fail: %s\n", STR_HELPER(name), strerror(errno)); \ return; \ } \ } #define GET_PARAMETER(name, v4l2id) void get_##name(int video_fd) \ { \ struct v4l2_control ctl = { 0 }; \ \ ctl.id = v4l2id; \ \ int rc = ioctl(video_fd, VIDIOC_G_CTRL, &ctl); \ if (rc < 0) { \ fprintf(stderr, "get %s fail: %s\n", STR_HELPER(name), strerror(errno)); \ return; \ } \ fprintf(stderr, "%s: %d\n", STR_HELPER(name), ctl.value); \ } int subscribe_array[] = { V4L2_CID_EXT_VSC_SUBSCRIBE_ACTIVE_WIN, V4L2_CID_EXT_VSC_SUBSCRIBE_APPLYING_DONE_WINDOW_REGION, V4L2_CID_EXT_VSC_SUBSCRIBE_BSP_ERROR, V4L2_CID_EXT_VSC_SUBSCRIBE_MUTE_OFF, V4L2_CID_EXT_VSC_SUBSCRIBE_VIDEO_DELAY, }; struct support_func { const char *name; void (*get_func)(int); void (*set_func)(int, char *); }; int get_num(char a) { if (a < '0') return 0; return a - '0'; } int get_big_num(char *start, int len) { char *p = start; int res = 0; for (int i = 0; i < len; i++) { if (*p == 0) break; res *= 10; res += get_num(*p); p++; } fprintf(stderr, "get big num is: %d\n", res); return res; } static int parse_rect(char *src, struct v4l2_ext_video_rect *out) // crop={12,34,56,78} { char *s = NULL, *e = NULL; char *p = NULL; s = strstr(src, "{"); e = strstr(src, "}"); p = s + 1; if (s == NULL || e == NULL) return -1; out->x = atoi(p); p = strstr(p, ","); p++; out->y = atoi(p); p = strstr(p, ","); p++; out->w = atoi(p); p = strstr(p, ","); p++; out->h = atoi(p); return 0; } static int get_rect_for_flag(char *src, const char *flag, struct v4l2_ext_video_rect *out) { char *p = NULL; if (out == NULL) return -1; p = strstr(src, flag); if (p == NULL) return -1; return parse_rect(p, out); } static int get_int_for_flag(char *src, const char *flag, int *value) { char *p = NULL; if (value == NULL) return -1; p = strstr(src, flag); if (p == NULL) return -1; p = strstr(p, "="); if (p == NULL) return -1; p++; *value = atoi(p); return 0; } static int get_char_for_flag(char *src, const char *flag, unsigned char *value) { int val = 0; int ret = get_int_for_flag(src, flag, &val); if (ret == 0) { if (val >= 0 && val <= 255) *value = (unsigned char)val; } return ret; } void set_win_region(int video_fd, char *par) { struct v4l2_ext_controls ext_controls = {0}; struct v4l2_ext_control ext_control = {0}; struct v4l2_ext_vsc_win_region win; int ret = 0; memset(&win, 0, sizeof(win)); ret |= get_rect_for_flag(par, "crop", &win.in.crop); ret |= get_rect_for_flag(par, "res", &win.in.res); ret |= get_rect_for_flag(par, "out", &win.out); ret |= get_rect_for_flag(par, "punch", &win.punch); ret |= get_int_for_flag(par, "rot", (int *)&win.rotation); if (ret) { fprintf(stderr, "par error\n"); return; } ext_controls.ctrl_class = V4L2_CTRL_CLASS_USER; ext_controls.count = 1; ext_controls.controls = &ext_control; ext_controls.controls->id = V4L2_CID_EXT_VSC_WIN_REGION; ext_controls.controls->ptr = (void *)&win; int rc; rc = ioctl(video_fd, VIDIOC_S_EXT_CTRLS, &ext_controls); if (rc < 0) { fprintf(stderr, "set win region fail: %s\n", strerror(errno)); return; } } void get_win_region(int video_fd) { struct v4l2_ext_controls ext_controls = {0}; struct v4l2_ext_control ext_control = {0}; struct v4l2_ext_vsc_win_region win; memset(&win, 0, sizeof(win)); ext_controls.ctrl_class = V4L2_CTRL_CLASS_USER; ext_controls.count = 1; ext_controls.controls = &ext_control; ext_controls.controls->id = V4L2_CID_EXT_VSC_WIN_REGION; ext_controls.controls->ptr = (void *)&win; int rc; rc = ioctl(video_fd, VIDIOC_G_EXT_CTRLS, &ext_controls); if (rc < 0) { fprintf(stderr, "get win region fail: %s\n", strerror(errno)); return; } fprintf(stderr, "win data: res {%d, %d, %d, %d}" " crop {%d, %d, %d, %d}" " out {%d, %d, %d, %d}" " punch {%d, %d, %d, %d}" " rot %d\n", win.in.res.x, win.in.res.y, win.in.res.w, win.in.res.h, win.in.crop.x, win.in.crop.y, win.in.crop.w, win.in.crop.h, win.out.x, win.out.y, win.out.w, win.out.h, win.punch.x, win.punch.y, win.punch.w, win.punch.h, win.rotation); } void set_connect_info(int video_fd, char *par) { struct v4l2_ext_controls ext_controls = {0}; struct v4l2_ext_control ext_control = {0}; struct v4l2_ext_vsc_connect_info con; int ret = 0; memset(&con, 0, sizeof(con)); ret |= get_int_for_flag(par, "attr", (int *)&con.in.attr); ret |= get_int_for_flag(par, "index", (int *)&con.in.index); ret |= get_int_for_flag(par, "src", (int *)&con.in.src); ret |= get_int_for_flag(par, "out", (int *)&con.out); if (ret) { fprintf(stderr, "par error\n"); return; } ext_control.id = V4L2_CID_EXT_VSC_CONNECT_INFO; ext_control.ptr = (void *)&con; ext_controls.ctrl_class = V4L2_CTRL_CLASS_USER; ext_controls.count = 1; ext_controls.controls = &ext_control; int rc; rc = ioctl(video_fd, VIDIOC_S_EXT_CTRLS, &ext_controls); if (rc < 0) { fprintf(stderr, "set connect info fail: %s\n", strerror(errno)); return; } } void get_connect_info(int video_fd) { struct v4l2_ext_controls ext_controls = {0}; struct v4l2_ext_control ext_control = {0}; struct v4l2_ext_vsc_connect_info con; memset(&con, 0, sizeof(con)); ext_control.id = V4L2_CID_EXT_VSC_CONNECT_INFO; ext_control.ptr = (void *)&con; ext_controls.ctrl_class = V4L2_CTRL_CLASS_USER; ext_controls.count = 1; ext_controls.controls = &ext_control; int rc; rc = ioctl(video_fd, VIDIOC_G_EXT_CTRLS, &ext_controls); if (rc < 0) { fprintf(stderr, "get connect info fail: %s\n", strerror(errno)); return; } fprintf(stderr, "connectinfo: attr %d index %d src %d out %d\n", con.in.attr, con.in.index, con.in.src, con.out); } void set_win_prop(int video_fd, char *par) { struct v4l2_ext_controls ext_controls = {0}; struct v4l2_ext_control ext_control = {0}; struct v4l2_ext_vsc_win_prop prop; memset(&prop, 0, sizeof(prop)); int ret = 0; ret |= get_int_for_flag(par, "win", (int *)&prop.win_mode); ret |= get_int_for_flag(par, "mirror", (int *)&prop.mirror_mode); ret |= get_int_for_flag(par, "mem", (int *)&prop.mem_type); if (ret != 0) { fprintf(stderr, "set win prop fail, par error\n"); return; } ext_control.id = V4L2_CID_EXT_VSC_WIN_PROP; ext_control.ptr = (void *)∝ ext_controls.ctrl_class = V4L2_CTRL_CLASS_USER; ext_controls.count = 1; ext_controls.controls = &ext_control; int rc; rc = ioctl(video_fd, VIDIOC_S_EXT_CTRLS, &ext_controls); if (rc < 0) { fprintf(stderr, "set win prop fail: %s\n", strerror(errno)); return; } } void get_win_prop(int video_fd) { struct v4l2_ext_controls ext_controls = {0}; struct v4l2_ext_control ext_control = {0}; struct v4l2_ext_vsc_win_prop prop; memset(&prop, 0, sizeof(prop)); ext_control.id = V4L2_CID_EXT_VSC_WIN_PROP; ext_control.ptr = (void *)∝ ext_controls.ctrl_class = V4L2_CTRL_CLASS_USER; ext_controls.count = 1; ext_controls.controls = &ext_control; int rc; rc = ioctl(video_fd, VIDIOC_G_EXT_CTRLS, &ext_controls); if (rc < 0) { fprintf(stderr, "get win prop fail: %s\n", strerror(errno)); return; } fprintf(stderr, "winprop: win mode %d mirror mode %d mem type %d\n", prop.win_mode, prop.mirror_mode, prop.mem_type); } void set_zorder(int video_fd, char *par) { struct v4l2_ext_controls ext_controls = { 0 }; struct v4l2_ext_control ext_control = { 0 }; struct v4l2_ext_vsc_zorder z = { 0 }; int ret = 0; ret |= get_char_for_flag(par, "alpha", &z.alpha); ret |= get_char_for_flag(par, "zorder", &z.zorder); if (ret != 0) { fprintf(stderr, "set zorder fail, par error\n"); return; } ext_control.id = V4L2_CID_EXT_VSC_ZORDER; ext_control.ptr = (void *)&z; ext_controls.ctrl_class = V4L2_CTRL_CLASS_USER; ext_controls.count = 1; ext_controls.controls = &ext_control; int rc = ioctl(video_fd, VIDIOC_S_EXT_CTRLS, &ext_controls); if (rc < 0) { fprintf(stderr, "set zorder fail: %s\n", strerror(errno)); return; } } void get_zorder(int video_fd) { struct v4l2_ext_controls ext_controls = { 0 }; struct v4l2_ext_control ext_control = { 0 }; struct v4l2_ext_vsc_zorder z = { 0 }; ext_control.id = V4L2_CID_EXT_VSC_ZORDER; ext_control.ptr = (void *)&z; ext_controls.ctrl_class = V4L2_CTRL_CLASS_USER; ext_controls.count = 1; ext_controls.controls = &ext_control; int rc = ioctl(video_fd, VIDIOC_G_EXT_CTRLS, &ext_controls); if (rc < 0) { fprintf(stderr, "get zorder fail: %s\n", strerror(errno)); return; } fprintf(stderr, "zorder: alpha %d, zorder %d\n", z.alpha, z.zorder); } SET_PARAMETER(hdr, V4L2_CID_EXT_VSC_HDR_TYPE) GET_PARAMETER(hdr, V4L2_CID_EXT_VSC_HDR_TYPE) SET_PARAMETER(freeze, V4L2_CID_EXT_VSC_FREEZE) GET_PARAMETER(freeze, V4L2_CID_EXT_VSC_FREEZE) SET_PARAMETER(freeze_frame, V4L2_CID_EXT_VSC_FREEZE_FRAME_BUFFER) GET_PARAMETER(freeze_frame, V4L2_CID_EXT_VSC_FREEZE_FRAME_BUFFER) SET_PARAMETER(frame_delay, V4L2_CID_EXT_VSC_FRAME_DELAY) GET_PARAMETER(frame_delay, V4L2_CID_EXT_VSC_FRAME_DELAY) SET_PARAMETER(rgb444, V4L2_CID_EXT_VSC_RGB444) GET_PARAMETER(rgb444, V4L2_CID_EXT_VSC_RGB444) SET_PARAMETER(occupation_sub_scaler, V4L2_CID_EXT_VSC_OCCUPATION_SUB_SCALER) GET_PARAMETER(occupation_sub_scaler, V4L2_CID_EXT_VSC_OCCUPATION_SUB_SCALER) SET_PARAMETER(adaptive_stream, V4L2_CID_EXT_VSC_ADAPTIVE_STREAM) GET_PARAMETER(adaptive_stream, V4L2_CID_EXT_VSC_ADAPTIVE_STREAM) SET_PARAMETER(bg_color, V4L2_CID_BG_COLOR) GET_PARAMETER(bg_color, V4L2_CID_BG_COLOR) GET_PARAMETER(hdmitx_connection, V4L2_CID_EXT_HDMITX_CONNECTION_STATE) GET_PARAMETER(hdmitx_hdr_mode, V4L2_CID_EXT_HDMITX_HDR_MODE) GET_PARAMETER(hdmitx_hdcp_version, V4L2_CID_EXT_HDMITX_HDCP_VERSION) SET_PARAMETER(hdmitx_hdr_policy, V4L2_CID_EXT_HDMITX_HDR_POLICY) GET_PARAMETER(hdmitx_hdr_policy, V4L2_CID_EXT_HDMITX_HDR_POLICY) SET_PARAMETER(hdmitx_av_mute, V4L2_CID_EXT_HDMITX_AVMUTE) SET_PARAMETER(hdmitx_hdcp_auth, V4L2_CID_EXT_HDMITX_HDCP_AUTH) GET_PARAMETER(hdmitx_hdcp_status, V4L2_CID_EXT_HDMITX_HDCP_STATUS) SET_PARAMETER(scan_mode, V4L2_CID_EXT_VSC_SCAN_MODE) GET_PARAMETER(scan_mode, V4L2_CID_EXT_VSC_SCAN_MODE) SET_PARAMETER(pixed_to_pixed, V4L2_CID_EXT_VSC_PIXEL_TO_PIXEL) GET_PARAMETER(pixed_to_pixed, V4L2_CID_EXT_VSC_PIXEL_TO_PIXEL) void set_orbit_policy(int video_fd, char *par) { struct v4l2_ext_controls ext_controls = {0}; struct v4l2_ext_control ext_control = {0}; struct v4l2_ext_vsc_orbit_policy policy; int ret = 0; unsigned char on_off = 0; struct v4l2_ext_video_rect t; struct v4l2_ext_video_rect x; memset(&policy, 0, sizeof(policy)); ret |= get_char_for_flag(par, "on", &on_off); ret |= get_rect_for_flag(par, "osdr", &policy.osd_resolution); ret |= get_rect_for_flag(par, "panelr", &policy.panel_resolution); ret |= get_rect_for_flag(par, "step", &t); ret |= get_rect_for_flag(par, "size", &x); ret |= get_int_for_flag(par, "vscan", (int *)&policy.region.video_scan); ret |= get_int_for_flag(par, "oscan", (int *)&policy.region.osd_scan); if (ret) { fprintf(stderr, "par error\n"); return; } policy.orbit_on_off = (enum v4l2_ext_vsc_orbit_on_off)on_off; policy.region.video_size.x = x.x; policy.region.video_size.y = x.y; policy.region.osd_size.x = x.w; policy.region.osd_size.y = x.h; policy.video_step.x = t.x; policy.video_step.y = t.y; policy.osd_step.x = t.w; policy.osd_step.y = t.h; ext_controls.ctrl_class = V4L2_CTRL_CLASS_USER; ext_controls.count = 1; ext_controls.controls = &ext_control; ext_controls.controls->id = V4L2_CID_EXT_VSC_ORBIT_POLICY; ext_controls.controls->ptr = (void *)&policy; int rc; rc = ioctl(video_fd, VIDIOC_S_EXT_CTRLS, &ext_controls); if (rc < 0) { fprintf(stderr, "set orbit policy fail: %s\n", strerror(errno)); return; } } void get_orbit_policy(int video_fd) { struct v4l2_ext_controls ext_controls = {0}; struct v4l2_ext_control ext_control = {0}; struct v4l2_ext_vsc_orbit_policy policy; memset(&policy, 0, sizeof(policy)); ext_controls.ctrl_class = V4L2_CTRL_CLASS_USER; ext_controls.count = 1; ext_controls.controls = &ext_control; ext_controls.controls->id = V4L2_CID_EXT_VSC_ORBIT_POLICY; ext_controls.controls->ptr = (void *)&policy; int rc; rc = ioctl(video_fd, VIDIOC_G_EXT_CTRLS, &ext_controls); if (rc < 0) { fprintf(stderr, "get orbit policy fail: %s\n", strerror(errno)); return; } fprintf(stderr, "on_off: %d, osd res: {%d, %d, %d, %d}" " panel res: {%d, %d, %d, %d}" " regin: {vs: {%d, %d}, vsan: %d, os: {%d, %d}, oscan: %d}" " vstep: {%d, %d}, ostep: {%d, %d}\n", policy.orbit_on_off, policy.osd_resolution.x, policy.osd_resolution.y, policy.osd_resolution.w, policy.osd_resolution.h, policy.panel_resolution.x, policy.panel_resolution.y, policy.panel_resolution.w, policy.panel_resolution.h, policy.region.video_size.x, policy.region.video_size.y, policy.region.video_scan, policy.region.osd_size.x, policy.region.osd_size.y, policy.region.osd_scan, policy.video_step.x, policy.video_step.y, policy.osd_step.x, policy.osd_step.y); } void set_seamless_info(int video_fd, char *par) { struct v4l2_ext_controls ext_controls = {0}; struct v4l2_ext_control ext_control = {0}; struct v4l2_ext_vsc_seamless_info info; int ret = 0; memset(&info, 0, sizeof(info)); ret |= get_char_for_flag(par, "cp", &info.cp_seamless); ret |= get_char_for_flag(par, "dtv", &info.dtv_seamless); ret |= get_char_for_flag(par, "ma", &info.magnification); if (ret) { fprintf(stderr, "par error\n"); return; } ext_control.id = V4L2_CID_EXT_VSC_SEAMLESS_MODE; ext_control.ptr = (void *)&info; ext_controls.ctrl_class = V4L2_CTRL_CLASS_USER; ext_controls.count = 1; ext_controls.controls = &ext_control; int rc; rc = ioctl(video_fd, VIDIOC_S_EXT_CTRLS, &ext_controls); if (rc < 0) { fprintf(stderr, "set seamless info fail: %s\n", strerror(errno)); return; } } void get_seamless_info(int video_fd) { struct v4l2_ext_controls ext_controls = {0}; struct v4l2_ext_control ext_control = {0}; struct v4l2_ext_vsc_seamless_info info; memset(&info, 0, sizeof(info)); ext_control.id = V4L2_CID_EXT_VSC_SEAMLESS_MODE; ext_control.ptr = (void *)&info; ext_controls.ctrl_class = V4L2_CTRL_CLASS_USER; ext_controls.count = 1; ext_controls.controls = &ext_control; int rc; rc = ioctl(video_fd, VIDIOC_G_EXT_CTRLS, &ext_controls); if (rc < 0) { fprintf(stderr, "get seamless info fail: %s\n", strerror(errno)); return; } fprintf(stderr, "seamless info: cp: %d, dtv: %d, magnification: %d\n", info.cp_seamless, info.dtv_seamless, info.magnification); } void get_event(int video_fd) { struct v4l2_event event; int rc; rc = ioctl(video_fd, VIDIOC_DQEVENT, &event); if (rc < 0) { fprintf(stderr, "get event fail: %s\n", strerror(errno)); return; } fprintf(stderr, "event is ok\n"); } void set_subscribe_event(int video_fd, char *par) { struct v4l2_event_subscription event = { 0 }; int id = 0; get_int_for_flag(par, "id", &id); event.type = V4L2_EVENT_CTRL; event.id = subscribe_array[id]; int rc = ioctl(video_fd, VIDIOC_SUBSCRIBE_EVENT, &event); if (rc < 0) { fprintf(stderr, "set event fail: %s\n", strerror(errno)); return; } } void get_alpha_zorder(int video_fd) { struct v4l2_ext_controls ext_controls = { 0 }; struct v4l2_ext_control ext_control = { 0 }; struct v4l2_ext_vsc_zorder_info z = { 0 }; ext_control.id = V4L2_CID_EXT_VSC_ALPHA_ZORDER; ext_control.ptr = (void *)&z; ext_controls.ctrl_class = V4L2_CTRL_CLASS_USER; ext_controls.count = 1; ext_controls.controls = &ext_control; int rc = ioctl(video_fd, VIDIOC_G_EXT_CTRLS, &ext_controls); if (rc < 0) { fprintf(stderr, "get zorder fail: %s\n", strerror(errno)); return; } fprintf(stderr, "zorder data length: %d\n", z.length); for (int i = 0; i < z.length; i++) fprintf(stderr, "zorder[%d]: alpha %d, zorder %d\n", i, z.z[i].alpha, z.z[i].zorder); } void set_alpha_zorder(int video_fd, char *par) { struct v4l2_ext_controls ext_controls = { 0 }; struct v4l2_ext_control ext_control = { 0 }; struct v4l2_ext_vsc_zorder_info z = { 0 }; int ret; ret = get_int_for_flag(par, "len", &z.length); for (int i = 0; i < z.length; i++) { ret |= get_char_for_flag(par, "alpha", &z.z[i].alpha); ret |= get_char_for_flag(par, "zorder", &z.z[i].zorder); } if (ret) { fprintf(stderr, "par error\n"); return; } ext_control.id = V4L2_CID_EXT_VSC_ALPHA_ZORDER; ext_control.ptr = (void *)&z; ext_controls.ctrl_class = V4L2_CTRL_CLASS_USER; ext_controls.count = 1; ext_controls.controls = &ext_control; int rc = ioctl(video_fd, VIDIOC_S_EXT_CTRLS, &ext_controls); if (rc < 0) { fprintf(stderr, "set zorder fail: %s\n", strerror(errno)); return; } } void test_video_play(int video_fd, int flag, int w, int h) { const char *video_info; char buffer[200] = { 0 }; if (flag == 0) { // local video/dtv video_info = "attr=0,index=0,src=4,out=1"; } else if (flag == 1) { // hdmi video_info = "attr=0,index=0,src=3,out=1"; } else if (flag == 2) { // VO video_info = "attr=0,index=0,src=5,out=1"; } else if (flag == 3) { // atv video_info = "attr=0,index=0,src=1,out=1"; } strcpy(buffer, video_info); set_connect_info(video_fd, buffer); snprintf(buffer, sizeof(buffer), "crop={0,0,%d,%d},res={0,0,%d,%d}," "out={0,0,3840,2160},punch={0,0,%d,%d},rot=0", w, h, w, h, w, h); set_win_region(video_fd, buffer); } struct ctx_info { int fd; bool exitThread; }; void* PollThreadMain(void *data) { struct ctx_info *ctx = (struct ctx_info *)data; int fd = ctx->fd; struct pollfd pfd; pfd.fd = fd; pfd.events = POLLIN; pfd.revents = 0; int pollrtn; fprintf(stderr, "PollThreadMain enter\n"); while (true) { if (ctx->exitThread) { pthread_exit(0); return NULL; } pollrtn = poll(&pfd, 1, 32); if (pollrtn > 0) { if (pfd.revents & POLLIN) { struct v4l2_event event; int rc = ioctl(fd, VIDIOC_DQEVENT, &event); if (rc < 0) { fprintf(stderr, "get event fail: %s\n", strerror(errno)); return NULL; } fprintf(stderr, "event id: %d\n", event.id); if (event.id == V4L2_CID_EXT_VSC_SUBSCRIBE_APPLYING_DONE_WINDOW_REGION) { fprintf(stderr, "scaler:%d, x:%d, y:%d, w:%d, h:%d, time:%d," " px:%d, py:%d, pw:%d, ph:%d\n", event.u.data[0], event.u.data[2] * 256 + event.u.data[1], event.u.data[4] * 256 + event.u.data[3], event.u.data[6] * 256 + event.u.data[5], event.u.data[8] * 256 + event.u.data[7], event.u.data[10] * 256 + event.u.data[9], event.u.data[12] * 256 + event.u.data[11], event.u.data[14] * 256 + event.u.data[13], event.u.data[16] * 256 + event.u.data[15], event.u.data[18] * 256 + event.u.data[17]); } if (event.id == V4L2_CID_EXT_VSC_SUBSCRIBE_BSP_ERROR) { fprintf(stderr, "bsp error: %s\n", event.u.data); } if (event.id == V4L2_CID_EXT_VSC_SUBSCRIBE_MUTE_OFF) { fprintf(stderr, "mute off\n"); } } } } } /* tunnel producer */ struct pro_buffer { int fd; void * img; int handle; int width; int height; int size; }; struct pro_data { int tunnel_fd; int tunnel_id; bool is_exit; pthread_t thread_id; int drmFd; struct pro_buffer buffer[BUFFER_MAX_NUMBER]; }; static int producer_alloc_buffer(struct pro_data *ctx, uint32_t width, uint32_t height) { if (!ctx) return -1; int drmfd = -1; struct drm_meson_gem_create arg; struct drm_mode_map_dumb map; struct pro_buffer *buffer = ctx->buffer; uint32_t stride; int ret; if (drmfd < 0) { drmfd = drmOpen("meson", NULL); if (drmfd < 0) { printf("failed to open drm: %s\n", strerror(errno)); return -1; } drmDropMaster(drmfd); } ctx->drmFd = drmfd; memset(&arg, 0, sizeof(arg)); memset(&map, 0, sizeof(map)); stride = width; arg.flags = MESON_USE_VIDEO_PLANE; arg.size = stride * height * 3 / 2; for (int i = 0 ;i < BUFFER_MAX_NUMBER; i++) { if (buffer[i].img) continue; memset(&buffer[i], 0, sizeof(buffer[i])); ret = ioctl(drmfd, DRM_IOCTL_MESON_GEM_CREATE, &arg); if (ret) { printf("alloc buffer fail: %s\n", strerror(errno)); return -1; } drmPrimeHandleToFD(drmfd, arg.handle, 0, &buffer[i].fd); printf("buffer info: handle: %d, patich: %d, size: %lld, fd: %d\n", arg.handle, width, arg.size, buffer[i].fd); printf("buffer info: width = %u, height = %u\n", width, height); map.handle = arg.handle; ret = drmIoctl(drmfd, DRM_IOCTL_MODE_MAP_DUMB, &map); if (ret) { printf("failed to map dumb buffer: %s\n",strerror(errno)); return -1; } printf("map offset: %lld, fd: %d, size: %lld\n", map.offset, drmfd, arg.size); buffer[i].img = mmap64(NULL, arg.size, PROT_READ | PROT_WRITE, MAP_SHARED, drmfd, map.offset); if (buffer[i].img == MAP_FAILED) { printf("failed to map buffer: %s\n", strerror(errno)); return -1; } memset(buffer[i].img, 0, arg.size); buffer[i].width = width; buffer[i].height = height; buffer[i].handle = arg.handle; buffer[i].size = arg.size; } return 0; } int producer_free_buffer(struct pro_data *ctx) { if (!ctx) return -1; struct pro_buffer *buffer = ctx->buffer; for (int i = 0; i < BUFFER_MAX_NUMBER; i++) { if (buffer[i].img) { munmap(buffer[i].img, buffer[i].size); buffer[i].img = NULL; } if (buffer[i].fd > 0) close(buffer[i].fd); if (buffer[i].handle > 0) { struct drm_mode_destroy_dumb arg; memset(&arg, 0, sizeof(arg)); arg.handle = buffer[i].handle; ioctl(ctx->drmFd, DRM_IOCTL_MODE_DESTROY_DUMB, &arg); printf("free handle: %d\n", arg.handle); } } return 0; } void fill_nv12_solid(uint8_t *data, int width, int height, uint8_t y_val, uint8_t u_val, uint8_t v_val) { if (!data || width <= 0 || height <= 0 || width % 2 != 0 || height % 2 != 0) { return; } int y_size = width * height; int uv_width = width / 2; int uv_height = height / 2; memset(data, y_val, y_size); uint8_t *uv_start = data + y_size; uint8_t *uv_line = (uint8_t *)malloc(width); for (int j = 0; j < uv_width; j++) { uv_line[j * 2] = u_val; uv_line[j * 2 + 1] = v_val; } for (int i = 0; i < uv_height; i++) { memcpy(uv_start + i * width, uv_line, width); } free(uv_line); } static void *vt_producer_thread(void *arg) { struct pro_data *ctx = (struct pro_data *)arg; int dequeue_fd = -1, fence_fd; int ret; int buffer_index = 0; meson_vt_connect(ctx->tunnel_fd, ctx->tunnel_id, 0); while (!ctx->is_exit) { meson_vt_queue_buffer(ctx->tunnel_fd, ctx->tunnel_id, ctx->buffer[buffer_index].fd, -1, VT_TIME_STAMP); fprintf(stderr, "queuebuffer fd:%d to tunnel:%d\n", ctx->buffer[buffer_index].fd, ctx->tunnel_id); sleep(1); dequeue_fd = -1; ret = meson_vt_dequeue_buffer(ctx->tunnel_fd, ctx->tunnel_id, &dequeue_fd, &fence_fd); fprintf(stderr, "dequeubuffer fd:%d from tunnel:%d, ret: %d\n", dequeue_fd, ctx->tunnel_id, ret); buffer_index++; if (buffer_index >= BUFFER_MAX_NUMBER) buffer_index = 0; } pthread_exit(0); return NULL; } static void start_producer(struct pro_data *ctx, int id) { int dev_fd = meson_vt_open(); printf("open videotunnel dev fd:%d\n", dev_fd); ctx->tunnel_fd = dev_fd; ctx->is_exit = false; ctx->tunnel_id = id; printf("alloc viduotunnel id:%d\n", ctx->tunnel_id); producer_alloc_buffer(ctx, 960, 540); fill_nv12_solid((uint8_t *)ctx->buffer[0].img, 960, 540, 76, 84, 255); // red fill_nv12_solid((uint8_t *)ctx->buffer[1].img, 960, 540, 149, 43, 21); // green fill_nv12_solid((uint8_t *)ctx->buffer[2].img, 960, 540, 29, 239, 102); // blue pthread_create(&ctx->thread_id, NULL, vt_producer_thread, ctx); } static void stop_producer(struct pro_data *ctx) { ctx->is_exit = true; if (ctx->thread_id) pthread_join(ctx->thread_id, NULL); meson_vt_disconnect(ctx->tunnel_fd, ctx->tunnel_id, 0); meson_vt_free_id(ctx->tunnel_fd, ctx->tunnel_id); meson_vt_close(ctx->tunnel_fd); producer_free_buffer(ctx); } struct support_func g_func[] = { { "WindowProp", get_win_prop, set_win_prop }, { "ConnectInfo", get_connect_info, set_connect_info }, { "WindowRegion", get_win_region, set_win_region }, { "Zorder", get_zorder, set_zorder }, { "Hdr", get_hdr, set_hdr }, { "Freeze", get_freeze, set_freeze }, { "Freeze_frame", get_freeze_frame, set_freeze_frame }, { "Frame_delay", get_frame_delay, set_frame_delay }, { "rgb444", get_rgb444, set_rgb444 }, { "occupation_sub_scaler", get_occupation_sub_scaler, set_occupation_sub_scaler }, { "adaptive_stream", get_adaptive_stream, set_adaptive_stream }, { "bg_color", get_bg_color, set_bg_color }, { "hdmitx_connection", get_hdmitx_connection, NULL }, { "hdmitx_hdr_mode", get_hdmitx_hdr_mode, NULL }, { "hdmitx_hdcp_version", get_hdmitx_hdcp_version, NULL }, { "hdmitx_hdr_policy", get_hdmitx_hdr_policy, set_hdmitx_hdr_policy }, { "hdmitx_av_mute", NULL, set_hdmitx_av_mute }, { "hdmitx_hdcp_auth", NULL, set_hdmitx_hdcp_auth }, { "hdmitx_hdcp_status", get_hdmitx_hdcp_status, NULL }, { "scan_mode", get_scan_mode, set_scan_mode }, { "pixed_to_pixed", get_pixed_to_pixed, set_pixed_to_pixed }, { "subscribe", NULL, set_subscribe_event }, { "alpha_zorder", get_alpha_zorder, set_alpha_zorder }, { "orbit_policy", get_orbit_policy, set_orbit_policy }, { "seamless_info", get_seamless_info, set_seamless_info } }; int same_name(char *name, const char *dst) { if (strlen(name) != strlen(dst)) return 1; if (strcmp(name, dst) != 0) return 1; return 0; } struct support_func *get_support_func(int index) { return &g_func[index]; } int get_func_len() { return sizeof(g_func) / sizeof(g_func[0]); } void exe_func(char *name, int flag, int fd, char *par) { struct support_func *func = NULL; int len = get_func_len(); int i; for (i = 0; i < len; i++) { func = get_support_func(i); if (func && same_name(name, func->name) == 0) { if (flag == 0 && func->get_func != NULL) func->get_func(fd); else if (flag == 1 && func->set_func != NULL) func->set_func(fd, par); } } } int main(int argc, char *argv[]) { int video_fd; int ch; char dev[NAME_LEN] = { 0 }; char func[FUNC_NAME_LEN] = { 0 }; char par[FUNC_NAME_LEN] = { 0 }; int flag = 0; int wait_flag = 0; int tunnel_flag = 0; struct ctx_info ctx = { 0 }; struct pro_data pctx = { 0 }; pthread_t thread_id; int v_flag = -1; while ((ch = getopt(argc, argv, "d:gsf:p:v:twh")) != -1) { switch (ch) { case 'd': strcpy(dev, optarg); break; case 'g': flag = 0; break; case 's': flag = 1; break; case 'f': strcpy(func, optarg); break; case 'p': strcpy(par, optarg); break; case 'w': wait_flag = 1; break; case 'v': wait_flag = 1; v_flag = atoi(optarg); break; case 't': wait_flag = 1; tunnel_flag = 1; break; case 'h': break; default: break; } } video_fd = open(dev, O_RDWR); if ( video_fd < 0) { fprintf(stderr, "%s: %s\n", dev, strerror(errno)); return -1; } if (wait_flag != 0) { char buffer[200] = { 0 }; char *tok, *tok_s = NULL; ctx.fd = video_fd; ctx.exitThread = false; int ret = pthread_create(&thread_id, NULL, PollThreadMain, &ctx); if (ret != 0) printf("create thread error: %d\n", errno); if (tunnel_flag > 0 && v_flag < 0) { test_video_play(video_fd, 0, 960, 540); start_producer(&pctx, 5); } if (v_flag >= 0) test_video_play(video_fd, v_flag, 1920, 1080); while (true) { if (fgets(buffer, sizeof(buffer), stdin) == NULL) continue; size_t len = strlen(buffer); if (len > 0 && buffer[len - 1] == '\n') { buffer[len - 1] = '\0'; } if (buffer[0] == 'q') break; tok = strtok_r(buffer, " ", &tok_s); if (tok) { if (!strncmp(tok, "get", 3)) flag = 0; else if (!strncmp(tok, "set", 3)) flag = 1; tok = strtok_r(0, " ", &tok_s); if (tok) { strcpy(func, tok); strcpy(par, tok_s); } } exe_func(func, flag, video_fd, par); } ctx.exitThread = true; pthread_join(thread_id, NULL); if (tunnel_flag > 0 && v_flag < 0) stop_producer(&pctx); } else { exe_func(func, flag, video_fd, par); } fprintf(stderr, "ioctl test success\n"); close(video_fd); return 0; }