OpenHD改造实现廉价高清数字图传(树莓派+PC)—(六)OSD和视频画面整合显示

2023-05-16

        这个OpenHD改造移植系列的最后一篇文章,这篇文章主要讲如何讲前面说到的全部内容串接起来,讲OSD画面显示和视频画面整合到一起,形成完整的图传地面显示,真正实现PC上直接接收显示图传视频和数据。

OpenHD改造实现廉价高清数字图传(树莓派+PC)—(一)概述_hoopertsau的博客-CSDN博客_openhd

OpenHD改造实现廉价高清数字图传(树莓派+PC)—(二)Wifibroadcast Wifi广播通信_hoopertsau的博客-CSDN博客

OpenHD改造实现廉价高清数字图传(树莓派+PC )—(三)OpenVG和libshapes在PC上的移植_hoopertsau的博客-CSDN博客

OpenHD改造实现廉价高清数字图传(树莓派+PC )—(四)OSD数据传输和画面显示_hoopertsau的博客-CSDN博客

OpenHD改造实现廉价高清数字图传(树莓派+PC)—(五)gstreamer视频采集、传输和显示_hoopertsau的博客-CSDN博客

一、OSD和视频整合显示改造

        整合OSD和视频显示核心思路:

        在X11window上创建两个窗口,一个用OpenVG来绘制OSD画面,一个用gstreamer来显示视频画面。将OSD画面的背景要设置为透明,并叠加显示在视频窗口上,这样就有浮在视频画面上的效果了。这里主要就在于怎么将OSD的背景设置为透明显示。

        这里主要修改的还是openvg目录下的oglinit.c文件,主要是创建窗口这里要修改。

        直接看一下代码,如何创建这样的两个窗口。

// 创建X11窗口
VGboolean windowCreate(STATE_T * state, const char* title,
                              const VGuint width,
                              const VGuint height)
{
    VGint screen, screenWidth, screenHeight;
    XSetWindowAttributes windowAttributes;
    XSizeHints windowSizeHints;

    XVisualInfo* visualInfo;
    MY_PFNGLXSWAPINTERVALSGIPROC sgiSwapInterval;
    MY_PFNGLXSWAPINTERVALEXTPROC extSwapInterval;
    // OpenGL surface configuration
	GLXFBConfig fbconfig;
    VGint fbConfigsCount = 0;
	
	static int glAttributes[] = {
		GLX_RENDER_TYPE, GLX_RGBA_BIT,
		GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
		GLX_DOUBLEBUFFER, True,
		GLX_RED_SIZE, 1,
		GLX_GREEN_SIZE, 1,
		GLX_BLUE_SIZE, 1,
		GLX_ALPHA_SIZE, 1,
		GLX_DEPTH_SIZE, 1,
		None
	};

    // open a display on the current root window
    display = XOpenDisplay(NULL);
    if (display == NULL) {
        fprintf(stderr, "Unable to open display.\n");
        return VG_FALSE;
    }

    // get the default screen associated with the previously opened display
    screen = DefaultScreen(display);
	// 根窗口
	rootWindow = RootWindow(display, screen);

    // get screen bitdepth
    screenDepth = DefaultDepth(display, screen);
    
    // run only on a 32bpp display
    if ((screenDepth != 24) && (screenDepth != 32)) {
        fprintf(stderr, "Cannot find 32bit pixel format on the current display.\n");
        XCloseDisplay(display);
        return VG_FALSE;
    }

    // get screen dimensions
    screenWidth = DisplayWidth(display, screen);
    screenHeight = DisplayHeight(display, screen);
	state->screen_width = screenWidth;
	state->screen_height = screenHeight;
	printf("screen width=%d height=%d\r\n",screenWidth,screenHeight);
	
	unsigned long white = WhitePixel(display,screen);
	unsigned long black = BlackPixel(display,screen);
	
				   
	// 再创建透明的OSD显示,在上层
	// 找到合适的配置
	GLXFBConfig* fbconfigs = glXChooseFBConfig(display, screen, glAttributes, &fbConfigsCount);
	int i = 0;
	static XRenderPictFormat *pictFormat;
    for(i = 0; i<fbConfigsCount; i++) {
        visualInfo = (XVisualInfo_CPP*) glXGetVisualFromFBConfig(display, fbconfigs[i]);
        if(!visualInfo)
            continue;

        pictFormat = XRenderFindVisualFormat(display, visualInfo->visual);
        if(!pictFormat)
            continue;

        if(pictFormat->direct.alphaMask > 0) {
            fbconfig = fbconfigs[i];
            break;
        }
    }

	// initialize window's attribute structure
    windowAttributes.colormap = XCreateColormap(display, rootWindow, visualInfo->visual, AllocNone);
	windowAttributes.border_pixel = 0;
	windowAttributes.background_pixmap = None;
	printf("None=%d\r\n",None);
	int attr_mask = 
        CWBackPixmap|
        CWColormap|
        CWBorderPixel;    /* What's in the attr data */
	
    // create the window centered on the screen
	state->window_width = width;
	state->window_height = height;
	
	// 先创建视频窗口,在底层
	video_window =  XCreateSimpleWindow(display,
									   rootWindow,
									   0, 0, // origin
									   width, height, // size
									   1, black, // border
									   white );  // backgd
				   
	// 创建OSD窗口
    window = XCreateWindow(display, rootWindow, 0,0, width, height, 1, visualInfo->depth, InputOutput, visualInfo->visual, 						attr_mask, &windowAttributes);
    //if ( window == None ) 
	if (window == None || video_window == None) 
	{
        fprintf(stderr, "Unable to create the window.\n");
        XCloseDisplay(display);
        return VG_FALSE;
    }
	
    // set the window's name
    XStoreName(display, window, title);
	XStoreName(display, video_window, "video window");
	
    // tell the server to report mouse and key-related events
    XSelectInput(display, window, KeyPressMask | KeyReleaseMask | ButtonPressMask | Button1MotionMask | Button2MotionMask | Button3MotionMask | StructureNotifyMask | ExposureMask);
	
    // initialize window's sizehint definition structure
    windowSizeHints.flags = PPosition | PMinSize | PMaxSize;
    windowSizeHints.x = 0;
    windowSizeHints.y = 0;
    windowSizeHints.min_width = 1;
    // clamp window dimensions according to the maximum surface dimension supported by the OpenVG backend
    windowSizeHints.max_width = vgPrivSurfaceMaxDimensionGetMZT();
    if (screenWidth < windowSizeHints.max_width) {
        windowSizeHints.max_width = screenWidth;
    }
    windowSizeHints.min_height = 1;
    windowSizeHints.max_height = windowSizeHints.max_width;
    if (screenHeight < windowSizeHints.max_height) {
        windowSizeHints.max_height = screenHeight;
    }
    // set the window's sizehint 不加这一句话,窗口的位置就不能正确设置
    XSetWMNormalHints(display, video_window, &windowSizeHints);
    // clear the window
    XClearWindow(display, video_window);
	
	// set the window's sizehint
    XSetWMNormalHints(display, window, &windowSizeHints);
    // clear the window
    XClearWindow(display, window);

    // create a GLX context for OpenGL rendering
    glContext = glXCreateNewContext(display, fbconfig, GLX_RGBA_TYPE, NULL, True);
    if (!glContext) {
        fprintf(stderr, "Unable to create the GLX context.\n");
		XDestroyWindow(display, video_window);
        XDestroyWindow(display, window);
        XCloseDisplay(display);
        return VG_FALSE;
    }
    // create a GLX window to associate the frame buffer configuration with the created X window
    glWindow = glXCreateWindow(display, fbconfig, window, NULL);
    // bind the GLX context to the Window
    glXMakeContextCurrent(display, glWindow, glWindow, glContext);
    // GLX_EXT_swap_control
    extSwapInterval = (MY_PFNGLXSWAPINTERVALEXTPROC)glXGetProcAddressARB((const GLubyte *)"glXSwapIntervalEXT");
    if (extSwapInterval) {
        extSwapInterval(display, glWindow, 0);
    }
    else {
        // GLX_SGI_swap_control
        sgiSwapInterval = (MY_PFNGLXSWAPINTERVALSGIPROC)glXGetProcAddressARB((const GLubyte *)"glXSwapIntervalSGI");
        if (sgiSwapInterval) {
            sgiSwapInterval(0);
        }
    }
	
    // put the window on top of the others
	XMapRaised(display, video_window);
    XMapRaised(display, window);
	
    // clear event queue
    XFlush(display);
    XFree(visualInfo);

/*
	// 无边框的效果
	Atom window_type = XInternAtom(display, "_NET_WM_WINDOW_TYPE", False);
	long value = XInternAtom(display, "_NET_WM_WINDOW_TYPE_DOCK", False);
	XChangeProperty(display, window, window_type, XA_ATOM, 32, PropModeReplace, (unsigned char *) &value,1 );
*/

	// 初始化视频显示
	gst_init (NULL,NULL);
	printf("gst inited\r\n");
	initGst(video_window);
	
    return VG_TRUE;
}

        分析一下这个窗口创建的代码,有几个关键的地方。

        1、找到合适的显示配置

	// 找到合适的配置
	GLXFBConfig* fbconfigs = glXChooseFBConfig(display, screen, glAttributes, &fbConfigsCount);
	int i = 0;
	static XRenderPictFormat *pictFormat;
    for(i = 0; i<fbConfigsCount; i++) {
        visualInfo = (XVisualInfo_CPP*) glXGetVisualFromFBConfig(display, fbconfigs[i]);
        if(!visualInfo)
            continue;

        pictFormat = XRenderFindVisualFormat(display, visualInfo->visual);
        if(!pictFormat)
            continue;

        if(pictFormat->direct.alphaMask > 0) {
            fbconfig = fbconfigs[i];
            break;
        }
    }

        2、窗口的属性设置

    // initialize window's attribute structure
    windowAttributes.colormap = XCreateColormap(display, rootWindow, visualInfo->visual, AllocNone);
	windowAttributes.border_pixel = 0;
	windowAttributes.background_pixmap = None;
    int attr_mask = 
        CWBackPixmap|
        CWColormap|
        CWBorderPixel;    /* What's in the attr data */

        要将背景设置为None,并且mask中要设置背景标志

        3、创建两个窗口

// 先创建视频窗口,在底层
	video_window =  XCreateSimpleWindow(display,
									   rootWindow,
									   0, 0, // origin
									   width, height, // size
									   1, black, // border
									   white );  // backgd
				   
	// 创建OSD窗口
    window = XCreateWindow(display, rootWindow, 0,0, width, height, 1, visualInfo->depth, InputOutput, visualInfo->visual,attr_mask, &windowAttributes);

        一个是用于显示视频的video_window ,一个是用于显示osd的

注意:

这两个的父窗口都是rootWindow,我尝试将osd的父窗口设置为视频,这样背景就不透明了。但是如果将视频窗口也设置为透明,gstreamer就显示不正常了,相互之间有影响。所以就干脆分开两个窗口,互不影响。

        4、设置窗口的位置

    // set the window's sizehint, 不加这一句话,窗口的位置就不能正确设置
    XSetWMNormalHints(display, video_window, &windowSizeHints);
    // clear the window
    XClearWindow(display, video_window);

        如果不设置的话,窗口的初始位置可能会有错位,导致两个窗口不能叠加在一起。

        5、初始化gstreamer并绑定GUI      

	// 初始化gstreamer库
	gst_init (NULL,NULL);
    // 创建视频处理的管道,并绑定到视频显示窗口video_window
	initGst(video_window);

          这个与上一篇文章的内容类似,就不再详细描述了,直接看之前的代码。

          看一下最终效果,叠在一起显示      

二、总结

        通过将OpenHD深度的剥离改造,可以实现在普通的PC上显示图传,大大降低我们的成本,不需要再买地面端的树莓派了。同时,OpenHD里面有大量“无用”的脚本和代码,过于复杂。

        其实用到的主要就是四个软件:

        1、wifibroadcast,提供底层的通信

        2、openvg,提供osd绘图的支撑

        3、gstreamer,osd调用并用来解析视频用的

        4、osd,接收解析并绘制视频和叠加显示信息

       

        当然OpenHD提供的是一揽子解决方案,直接烧写镜像即可开机使用,比较适合不会开发的航模玩家,可以直接上手使用

三、后续

        考虑到天空端的树莓派Zero也开始涨价了,当前价格大概在500块左右(2022年9月30日),为了进一步降低成本,我还考虑将天空端移植到国产的PI上。

        这里有几个可选的PI,后续再讲如何往这些国产化的PI上进行迁移,进一步降低成本

        1、OrangePi Zero

                全志的H3处理器,只要大概120块钱。但是没有CSI接口的摄像头,需要外接使用USB摄像头。

        

        2、OrangePi i96

        RDA8810PL处理器,非常便宜,开发板只要39块钱!摄像头32块。当然,性能不是太好,而且CPU也很老了,也很难找到技术支持,而且坑也比较多。但是架不住它便宜啊,哈哈。

        3、Banana pi-M2 Zero

                使用的也是全志H3处理器,价格140左右,但是摄像头贵了,要60块钱。

        

        

        

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

OpenHD改造实现廉价高清数字图传(树莓派+PC)—(六)OSD和视频画面整合显示 的相关文章

  • 计算机类期刊投稿心得 [ 添加中...现35种 ]

    1 杂志名称 计算机应用研究 杂志文章包含专业 建模 xff0c 仿真 xff0c 网络 xff0c 人工智能 xff0c 比较杂 投稿联系方式 http www arocmag com 注册在线投稿审稿 投稿费用 250元 页 杂志级别
  • Minix下的汇编

    Minix下的汇编 大多数的编译器 xff0c 如Turbo C C 43 43 xff0c Borland C C 43 43 xff0c M C C 43 43 xff0c GCC xff0c VC 43 xff0c 编译过程都是 xf

随机推荐

  • 解决Xshell 7 报错 “要继续使用此程序,您必须应用最新的更新或使用新版本”

    1 先创建一个文本文档 xff0c 同时把该文档名称和后缀改为xshell7 bat xff1b 2 打开编辑这个xshell7 bat文件 xff0c 并且把以下文字复制进去 xff0c 注意set XSHELL 61 这一项需要改成你自
  • 多任务操作系统的任务切换

    在学习OS时 xff0c 对于多任务操作系统的任务切换 xff0c 一直不能理解 xff1a 控制权是怎么么回到调度程序上的 xff1f 记得在描述任务切换时 xff0c 一般都是这么描述的 xff1a 在每一个时钟滴答 xff0c 都将检
  • Minix下的汇编2

    似乎minix平台并没有带一个真正的汇编编译器 xff0c 看看makefile xff0c 几乎都是清一色的用cc来编译汇编代码的 而且 xff0c 即使是一个最简单功能的汇编程序 xff0c 也少不了一个 main 标签 在minix的
  • 原来在/var/spool/mail中

    fetchmail会把从mail server收到的邮件投递到 var spool mail 中去 而mutt也会自动地到 var spool mail里取信 xff0c 解码 xff0c 并显示 但 xff0c fetchmail的速度不
  • 汉字编码标准与识别(一)代码页(Code Page)初识

    BBS水木清华站 精华区 发信人 yanglc 魂归燕园 别理我 xff0c 烦着呢 信区 Linux 标 题 汉字编码标准与识别 一 发信站 BBS 水木清华站 Sat Apr 29 17 19 05 2000 http www linu
  • 让xpdf支持中文(C++primer中文版)

    首先到http www linuxfans org nuke modules php name 61 Site Downloads amp op 61 geninfo amp did 61 2385下载一个打了补丁的xpdf 安装 xff0
  • Xpdf-3 for MDK

    http www linuxfans org nuke modules php name 61 Site Downloads amp op 61 geninfo amp did 61 2385 Xpdf 3 for MDK 类别 其它软件
  • 不同公司的牛

    本文转自 C 43 43 Builder 研究 http www ccrun com other go asp i 61 264 amp d 61 sgz5id 传统公司 xff1a 你有两头母牛 你卖掉一头 xff0c 买了一头公牛 你的
  • 从词法分析开始

    刚开始时 xff0c 用lex的确是很方便 xff0c 但是这样却不能将词法分析的思想实践出来 最好的方法还是自己写一个lex 当然龙书上写得很详细了 xff0c 但是写得再详细 xff0c 把它实现出来还是很难的 我的计划是 xff1a
  • Python 获取当前路径几种方法

    Python 获取当前路径的几种方法 绝对路径 1 os path 方法 span class token comment coding utf 8 span span class token comment usr bin python
  • [pixhawk笔记]2-飞行模式

    本文翻译自px4官方开发文档 xff1a https dev px4 io en concept flight modes html xff0c 有不对之处 xff0c 敬请指正 pixhawk的飞行模式如下 xff1a MANUAL xf
  • 扩展卡尔曼滤波详解

    Extened Kalman Filter 简单介绍 卡尔曼滤波详解讲解的基本的卡尔曼滤波算法是通过一个线性随机差分方程来估计当前状态 xff0c 但是如果状态估计关系以及测量关系使非线性的怎么办 xff0c 而且在实际使用中大部分的问题都
  • 关于PX4中的高度若干问题

    飞行的高度是如何测量的 xff1f 地面的高度和海平面的高度差别很大 xff0c 飞控又是如何有效判别进行降落的 xff1f 这是我脑子里的疑问 搜索的一圈发现很少有人讨论这方面的问题 xff0c 于是本次我就直接看一下源代码 xff0c
  • 基于4G网卡和树莓派zero实现低延时数字图传(250-300ms左右)

    方案本身并不复杂 xff0c 都是采用成熟的产品 xff0c 只需要几个命令行就能解决问题 0 准备工作 硬件 xff1a 树莓派zero 4G网卡 linux台式机 笔记本 虚拟机 软件 xff1a raspivid netcat nc
  • 树莓派zero w 使用AV接口连接电视机

    树莓派zero本身板子上有一个mini HDMI xff0c 但是我看到好像板子上还有一个小接口 xff0c 上面写着TV xff0c 感觉应该可以输出AV信号 xff0c 于是网上搜索了一番 xff0c 果然可以 首先 xff0c 手工做
  • OpenHD改造实现廉价高清数字图传(树莓派+PC)—(一)概述

    一 缘由 数字图传网上有开源的解决方案 xff0c 最为出名的应该就是OpenHD了 如果按照官方网站的内容 xff0c 构建起来也不是很复杂 xff0c 直接可以烧录两个TF卡就能完成 但是 xff0c 你需要用到两个树莓派板卡 xff0
  • OpenHD改造实现廉价高清数字图传(树莓派+PC)—(二)Wifibroadcast Wifi广播通信

    上一篇文章重点介绍了数字图传的整体构建思路 xff0c 以及主要的软件模块和最终效果 接下来几篇文章将针对其中的几个主要关键技术点进行阐述 一方面是为了将这些知识点做一个整理记录 xff0c 方便后续查阅 xff0c 另一方面也是将学习到知
  • OpenHD改造实现廉价高清数字图传(树莓派+PC )—(四)OSD数据传输和画面显示

    前面三篇文章分别讲了整体情况 xff0c wifibroadcast xff0c 以及OpenVG的移植等 OpenHD改造实现廉价高清数字图传 xff08 树莓派zero 43 ubuntu PC xff09 xff08 一 xff09
  • OpenHD改造实现廉价高清数字图传(树莓派+PC)—(五)gstreamer视频采集、传输和显示

    图传的一个重要功能就是可以看视频 主要是采集树莓派zero摄像头的数据 xff0c 经过编码打包 xff0c 通过wifibroadcast发送到地面端的PC上 xff0c 然后再通过解码显示出来 这里用到了视频采集和编解码相关的软件 在树
  • OpenHD改造实现廉价高清数字图传(树莓派+PC)—(六)OSD和视频画面整合显示

    这个OpenHD改造移植系列的最后一篇文章 xff0c 这篇文章主要讲如何讲前面说到的全部内容串接起来 xff0c 讲OSD画面显示和视频画面整合到一起 xff0c 形成完整的图传地面显示 xff0c 真正实现PC上直接接收显示图传视频和数