1113 lines
33 KiB
C++
1113 lines
33 KiB
C++
/*
|
|
* 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 <stdio.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <poll.h>
|
|
#include <pthread.h>
|
|
|
|
#include <linux/videodev2.h>
|
|
|
|
#include "vsc_fuse.h"
|
|
#include <linux/v4l2-ext-vsc.h>
|
|
#include <linux/v4l2-ext-hdmitx.h>
|
|
|
|
#include <video_tunnel.h>
|
|
#include <meson_drm.h>
|
|
#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;
|
|
}
|