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


            这个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文件,主要是创建窗口这里要修改。

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

    1. // 创建X11窗口
    2. VGboolean windowCreate(STATE_T * state, const char* title,
    3. const VGuint width,
    4. const VGuint height)
    5. {
    6. VGint screen, screenWidth, screenHeight;
    7. XSetWindowAttributes windowAttributes;
    8. XSizeHints windowSizeHints;
    9. XVisualInfo* visualInfo;
    10. MY_PFNGLXSWAPINTERVALSGIPROC sgiSwapInterval;
    11. MY_PFNGLXSWAPINTERVALEXTPROC extSwapInterval;
    12. // OpenGL surface configuration
    13. GLXFBConfig fbconfig;
    14. VGint fbConfigsCount = 0;
    15. static int glAttributes[] = {
    16. GLX_RENDER_TYPE, GLX_RGBA_BIT,
    17. GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
    18. GLX_DOUBLEBUFFER, True,
    19. GLX_RED_SIZE, 1,
    20. GLX_GREEN_SIZE, 1,
    21. GLX_BLUE_SIZE, 1,
    22. GLX_ALPHA_SIZE, 1,
    23. GLX_DEPTH_SIZE, 1,
    24. None
    25. };
    26. // open a display on the current root window
    27. display = XOpenDisplay(NULL);
    28. if (display == NULL) {
    29. fprintf(stderr, "Unable to open display.\n");
    30. return VG_FALSE;
    31. }
    32. // get the default screen associated with the previously opened display
    33. screen = DefaultScreen(display);
    34. // 根窗口
    35. rootWindow = RootWindow(display, screen);
    36. // get screen bitdepth
    37. screenDepth = DefaultDepth(display, screen);
    38. // run only on a 32bpp display
    39. if ((screenDepth != 24) && (screenDepth != 32)) {
    40. fprintf(stderr, "Cannot find 32bit pixel format on the current display.\n");
    41. XCloseDisplay(display);
    42. return VG_FALSE;
    43. }
    44. // get screen dimensions
    45. screenWidth = DisplayWidth(display, screen);
    46. screenHeight = DisplayHeight(display, screen);
    47. state->screen_width = screenWidth;
    48. state->screen_height = screenHeight;
    49. printf("screen width=%d height=%d\r\n",screenWidth,screenHeight);
    50. unsigned long white = WhitePixel(display,screen);
    51. unsigned long black = BlackPixel(display,screen);
    52. // 再创建透明的OSD显示,在上层
    53. // 找到合适的配置
    54. GLXFBConfig* fbconfigs = glXChooseFBConfig(display, screen, glAttributes, &fbConfigsCount);
    55. int i = 0;
    56. static XRenderPictFormat *pictFormat;
    57. for(i = 0; i
    58. visualInfo = (XVisualInfo_CPP*) glXGetVisualFromFBConfig(display, fbconfigs[i]);
    59. if(!visualInfo)
    60. continue;
    61. pictFormat = XRenderFindVisualFormat(display, visualInfo->visual);
    62. if(!pictFormat)
    63. continue;
    64. if(pictFormat->direct.alphaMask > 0) {
    65. fbconfig = fbconfigs[i];
    66. break;
    67. }
    68. }
    69. // initialize window's attribute structure
    70. windowAttributes.colormap = XCreateColormap(display, rootWindow, visualInfo->visual, AllocNone);
    71. windowAttributes.border_pixel = 0;
    72. windowAttributes.background_pixmap = None;
    73. printf("None=%d\r\n",None);
    74. int attr_mask =
    75. CWBackPixmap|
    76. CWColormap|
    77. CWBorderPixel; /* What's in the attr data */
    78. // create the window centered on the screen
    79. state->window_width = width;
    80. state->window_height = height;
    81. // 先创建视频窗口,在底层
    82. video_window = XCreateSimpleWindow(display,
    83. rootWindow,
    84. 0, 0, // origin
    85. width, height, // size
    86. 1, black, // border
    87. white ); // backgd
    88. // 创建OSD窗口
    89. window = XCreateWindow(display, rootWindow, 0,0, width, height, 1, visualInfo->depth, InputOutput, visualInfo->visual, attr_mask, &windowAttributes);
    90. //if ( window == None )
    91. if (window == None || video_window == None)
    92. {
    93. fprintf(stderr, "Unable to create the window.\n");
    94. XCloseDisplay(display);
    95. return VG_FALSE;
    96. }
    97. // set the window's name
    98. XStoreName(display, window, title);
    99. XStoreName(display, video_window, "video window");
    100. // tell the server to report mouse and key-related events
    101. XSelectInput(display, window, KeyPressMask | KeyReleaseMask | ButtonPressMask | Button1MotionMask | Button2MotionMask | Button3MotionMask | StructureNotifyMask | ExposureMask);
    102. // initialize window's sizehint definition structure
    103. windowSizeHints.flags = PPosition | PMinSize | PMaxSize;
    104. windowSizeHints.x = 0;
    105. windowSizeHints.y = 0;
    106. windowSizeHints.min_width = 1;
    107. // clamp window dimensions according to the maximum surface dimension supported by the OpenVG backend
    108. windowSizeHints.max_width = vgPrivSurfaceMaxDimensionGetMZT();
    109. if (screenWidth < windowSizeHints.max_width) {
    110. windowSizeHints.max_width = screenWidth;
    111. }
    112. windowSizeHints.min_height = 1;
    113. windowSizeHints.max_height = windowSizeHints.max_width;
    114. if (screenHeight < windowSizeHints.max_height) {
    115. windowSizeHints.max_height = screenHeight;
    116. }
    117. // set the window's sizehint 不加这一句话,窗口的位置就不能正确设置
    118. XSetWMNormalHints(display, video_window, &windowSizeHints);
    119. // clear the window
    120. XClearWindow(display, video_window);
    121. // set the window's sizehint
    122. XSetWMNormalHints(display, window, &windowSizeHints);
    123. // clear the window
    124. XClearWindow(display, window);
    125. // create a GLX context for OpenGL rendering
    126. glContext = glXCreateNewContext(display, fbconfig, GLX_RGBA_TYPE, NULL, True);
    127. if (!glContext) {
    128. fprintf(stderr, "Unable to create the GLX context.\n");
    129. XDestroyWindow(display, video_window);
    130. XDestroyWindow(display, window);
    131. XCloseDisplay(display);
    132. return VG_FALSE;
    133. }
    134. // create a GLX window to associate the frame buffer configuration with the created X window
    135. glWindow = glXCreateWindow(display, fbconfig, window, NULL);
    136. // bind the GLX context to the Window
    137. glXMakeContextCurrent(display, glWindow, glWindow, glContext);
    138. // GLX_EXT_swap_control
    139. extSwapInterval = (MY_PFNGLXSWAPINTERVALEXTPROC)glXGetProcAddressARB((const GLubyte *)"glXSwapIntervalEXT");
    140. if (extSwapInterval) {
    141. extSwapInterval(display, glWindow, 0);
    142. }
    143. else {
    144. // GLX_SGI_swap_control
    145. sgiSwapInterval = (MY_PFNGLXSWAPINTERVALSGIPROC)glXGetProcAddressARB((const GLubyte *)"glXSwapIntervalSGI");
    146. if (sgiSwapInterval) {
    147. sgiSwapInterval(0);
    148. }
    149. }
    150. // put the window on top of the others
    151. XMapRaised(display, video_window);
    152. XMapRaised(display, window);
    153. // clear event queue
    154. XFlush(display);
    155. XFree(visualInfo);
    156. /*
    157. // 无边框的效果
    158. Atom window_type = XInternAtom(display, "_NET_WM_WINDOW_TYPE", False);
    159. long value = XInternAtom(display, "_NET_WM_WINDOW_TYPE_DOCK", False);
    160. XChangeProperty(display, window, window_type, XA_ATOM, 32, PropModeReplace, (unsigned char *) &value,1 );
    161. */
    162. // 初始化视频显示
    163. gst_init (NULL,NULL);
    164. printf("gst inited\r\n");
    165. initGst(video_window);
    166. return VG_TRUE;
    167. }

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

            1、找到合适的显示配置

    1. // 找到合适的配置
    2. GLXFBConfig* fbconfigs = glXChooseFBConfig(display, screen, glAttributes, &fbConfigsCount);
    3. int i = 0;
    4. static XRenderPictFormat *pictFormat;
    5. for(i = 0; i
    6. visualInfo = (XVisualInfo_CPP*) glXGetVisualFromFBConfig(display, fbconfigs[i]);
    7. if(!visualInfo)
    8. continue;
    9. pictFormat = XRenderFindVisualFormat(display, visualInfo->visual);
    10. if(!pictFormat)
    11. continue;
    12. if(pictFormat->direct.alphaMask > 0) {
    13. fbconfig = fbconfigs[i];
    14. break;
    15. }
    16. }

            2、窗口的属性设置

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

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

            3、创建两个窗口

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

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

    注意:

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

            4、设置窗口的位置

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

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

            5、初始化gstreamer并绑定GUI      

    1. // 初始化gstreamer库
    2. gst_init (NULL,NULL);
    3. // 创建视频处理的管道,并绑定到视频显示窗口video_window
    4. 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块钱。

            

            

            

  • 相关阅读:
    IP代理|一文看懂IPv4与IPv6
    markdown语法(更新中)
    python28种极坐标绘图函数总结
    千元级3D打印机爆发在即 全民3D打印的春天快来了?
    端口转发及防火墙过滤设置
    第二章 基础知识(5) - 异常处理
    QoS(服务质量)学习记录
    群晖7.2版本安装Jellyfin
    【VMware虚拟机使用记录】—— 虚拟机开启失败的问题分析及解决方法
    GBase 8c 核心技术
  • 原文地址:https://blog.csdn.net/hoopertsau/article/details/127121472