GUI по-русски, или ВКС терминал своими руками

    Опыт разработки GUI на С++ для российской системы видеоконференцсвязи (ВКС). Синтез современных технологий и требований сертификации. Главные «грабли» разработки и пути их обхода. Что общего у GUI и русского балета.

    Первое, что видит пользователь ВКС-системы – это интерфейс. И в большинстве случаев именно по его внешнему виду и функционалу судят о системе. Неудобный или расползающийся интерфейс не позволит оценить ни высокую производительность системы, ни широкий функционал. Технически «красивая» система должна быть обернута в привлекательную и стабильно работающую оболочку. Поэтому при старте разработки отечественной ВКС системы этот момент был сразу учтен.

    image

    Кто будет пользователем российской ВКС?


    С весны 2020 года ответ на вопрос о целесообразности разработки полноценной ВКС-системы стал очевиден. Для чиновников, коммерческих компаний, больниц и школ нужны современные средства связи с определенным уровнем производительности и защищенности. Пообщаться можно и в Zoom, но стоит ли его использовать для серьезных коммерческих переговоров или оперативного совещания руководителей?

    Для различных задач российских компаний стало необходимо создание отечественной ВКС-системы. Причем системы, состоящей не только из программной составляющей, но и полноценной аппаратной части. Среди известных мировых вендоров как минимум 5 компаний предлагают многофункциональные системы ВКС. Но в России концепция импортозамещения постепенно начинает работать. Плюс вопросы безопасности для многих стали важнее страны происхождения продукта, да и цена при нынешних курсах валют не на последнем месте. А «красоту» интерфейса оказалось вполне реально разработать с нуля.

    GUI на старте


    Главными требованиями к современным интерфейсам являются быстрота реализации, актуальный внешний вид и полноценное юзабилити. Таким образом, первой задачей разработчиков графического пользовательского интерфейса (GUI) стало четкое определение функционала ПО для ВКС.

    С точки зрения GUI требования были сформулированы следующие:

    • Осуществление исходящих видео/аудио вызовов;
    • Прием/отклонение входящих вызовов;
    • Автоответ на входящий вызов через настраиваемый интервал времени;
    • Переключение между двумя аудиоустройствами (гарнитура/ громкая связь) как во время, так и вне вызова;
    • Включение/выключение микрофона и камеры как во время, так и вне вызова;
    • DTMF-набор во время вызова;
    • Сбор конференции на терминале;
    • Управление PTZ-камерами, сохранение PTZ-пресетов и применение их;
    • Возможность вывода видео в несколько различных окон;
    • Управление мышью, клавиатурой, пультом дистанционного управления;
    • Возможность удалённого управления терминалом из Web-интерфейса.

    Такой список функций позволяет решать задачу разработки интерфейса различными способами. Причем на выбор конкретного типа реализации повлияли ограничения типа языков программирования (например, Java категорически не подходил по соображениям сертификации, CSS/HTML — по функционалу), специализация разработчиков и сроки. По совокупности выбор был сделан в пользу C++ и использования фрэймворка Qt5, так как, например, более современная технология QML не позволяет рендерить видео с использованием произвольного OpenGL-контекста, а это было необходимо по ТЗ для терминалов ВКС.

    image

    Быстро и качественно


    Первый прототип GUI создавался для софтфона на Qt и использовал множество opensource-библиотек. Например, для протокола SIP использовались библиотеки eXosip/oSIP, для кодирования/декодирования видео и аудио – ffmpeg, для работы с аудиоустройствами – PortAudio. Данный софтфон работал под Linux, Windows, MacOS и являлся демонстратором технологий, а не реальным устройством.

    Позже абстрактный софтфон трансформировался в реальный проект видеотелефона, причем первая версия ПО для него должна была быть создана уже через 2 месяца после старта. Для решения этой задачи в столь сжатые сроки ПО телефона было разделено на модули и распределено между несколькими группами разработчиков в соответствии с компетенциями. Такая организация процесса помогла быстро и качественно развивать проект видеотелефона.

    Core and Front


    Для унификации и возможности использования уже имеющихся GUI-разработок в других устройствах из действующего проекта общую кодовую базу в отдельный модуль – бэкэнд GUI, или модуль GUI core. А непосредственно представления, которые различны у разных устройств, реализовывать в отдельных модулях GUI front.

    Такая архитектура GUI-модулей оказалась выигрышной и привела к нужному результату: разработка интерфейсов для новых компонентов собственно системы ВКС стала относительно быстрой и качественной. Ведь теперь интерфейсы для терминалов ВКС не нужно было переписывать с нуля.

    Муки и победы


    На пути создания любого ПО, естественно, встречаются свои сложности и проблемы. Создание GUI для ВКС не стало исключением. Вне зависимости от конкретного назначения системы они могут повторяться в любой команде. Трудности и победы на пути разработки интересны коллегам, и возможно, подскажут эффективные пути решения без наших «граблей».

    Консистентность forever


    Исторически самой первой интересной проблемой, которая возникла ещё при разработке GUI для различных типов терминалов ВКС, стала проблема консистентности, то есть согласованного состояния всех модулей. В процессе функционирования GUI взаимодействует с несколькими другими модулями: модулем взаимодействия с аппаратными средствами, подсистемой управления вызовами, модулем об работки медиа (MCU) и подсистемой взаимодействия с пользователем.

    image

    Изначально GUI работало со всеми этими модулями как с независимыми, то есть могло одновременно отправить запросы в 2 разных модуля. Это оказалось неправильным и иногда приводило к проблемам, так как сами эти модули не являлись независимыми и активно взаимодействовали между собой. Решением проблемы оказалось создание специальной схемы работы, при которой обеспечивалось строго последовательное выполнение запросов в рамках всех модулей.

    Сложностей добавляло 2 момента: во-первых, некоторые (но не все) запросы требуют ответа, в ожидании которого терминал, по сути, находится в неконсистентном состоянии, поэтому другие запросы выполнять нельзя. Однако блокировать на время ожидания ответов пользовательский интерфейс тоже нежелательно. Во-вторых, ответы на запросы GUI от модулей, а также запросы со стороны модулей к GUI приходят в своих собственных потоках, отличных от GUI, но GUI должно все изменения своего состояния осуществлять в своем потоке (для некоторых действий этого требует Qt, а в некоторых случаях это позволяет избежать лишних сложностей при обеспечении синхронизации потоков).

    Решение было найдено и состояло из двух частей. Сначала все запросы/ответы от других модулей перенаправлялись в GUI-поток с использованием механизма сигналов-слотов Qt в совокупности с QueuedConnection, то есть с использованием глобального цикла событий QApplication. Затем для обеспечения последовательной обработки всех запросов была разработана система “переходов” (Transitions) с собственной очередью и циклом обработки (TransitionLoop).

    Таким образом, когда пользователь нажимает какую-нибудь кнопку действия в GUI (например, кнопку вызова), создается соответствующий Transition, который помещается в очередь переходов. После этого генерируется сигнал для цикла обработки переходов. TransitionLoop, получив сигнал, смотрит, есть ли сейчас какой-то переход в состоянии выполнения. Если есть, то продолжается ожидание завершения текущего перехода; если нет, то из очереди переходов извлекается следующий Transition и запускается на выполнение. При получении ответа от другого модуля TransitionLoop с помощью такого же сигнала извещается о завершении текущего перехода и TransitionLoop может запустить следующий переход из очереди.

    Важным здесь является то, что вся обработка переходов осуществляется в GUI-потоке. Это обеспечивается применением механизма сигналов-слотов Qt в варианте QueuedConnection, при котором для каждого сигнала создаётся событие, помещающееся в главный EventLoop приложения.

    OpenGL-рендеринг на маломощном железе


    Еще одной трудностью, с которой пришлось столкнуться, стала проблема рендеринга видео. Qt предоставляет для OpenGL-рендеринга специальный класс QOpenGLWidget и соответствующие вспомогательные классы, который изначально и был использован для рендеринга видео. Сами данные для рендеринга (декодированные кадры видео) предоставляет модуль обработки медиа (MCU), который, в том числе реализует аппаратное декодирование видеопотока (на GPU). На маломощных процессорах было обнаружено “подтормаживание” рендеринга FullHD-видео. Прямым решением была замена процессора, но это потребовало бы серьезной переработки уже готовых компонентов системы ВКС и повысило бы стоимость самих устройств. Поэтому весь процесс рендеринга был тщательно проанализирован для поиска более красивых путей решения проблемы.

    При стандартном OpenGL-рендеринге и аппаратном декодировании происходит следующее: из сети приходят данные с закодированным видео, они сохраняются в оперативной памяти, затем эти данные из оперативной памяти переносятся в видеопамять на GPU, там они декодируются. Затем декодированные данные, имеющие существенно больший объём, чем закодированные, переносятся опять в оперативную память. Далее в работу вступает код рендеринга, который эти данные переносит из оперативной памяти обратно в GPU непосредственно для рендеринга. Таким образом по шине памяти туда-сюда перекачиваются довольно большие объемы данных, и просто-напросто шина с этим не справляется.

    В современных версиях OpenGL есть специальные расширения, позволяющие указать для рендеринга данные, уже находящиеся в памяти GPU, а не данные в основной оперативной памяти, как обычно. Этот механизм исключал перемещение данных аппаратно декодированных кадров из GPU в оперативную память, а потом обратно. Таким образом проблема рендеринга на маломощных процессорах была почти решена.

    image

    Другой серьезной проблемой стали OpenGL-контексты, поддерживаемые в Qt. Они не позволяют использовать необходимое расширение OpenGL, то есть использовать QOpenGLWidget при таком варианте нельзя. Решением стало использовать обычный QWidget, но выключенный из конвейера рендеринга Qt. Такая возможность в Qt существует. Однако и здесь возник вопрос, ведь в таком варианте за всю отрисовку в области данного виджета полностью отвечает GUI, Qt нам ничем не помогает. Для отображения видео это нормально, но для отображения виджетов поверх видео штатные средства Qt использовать невозможно, так как поверх видео необходимо отображать, например, дополнительное всплывающее меню.

    Эта проблема была решена так: из существующего виджета получается его изображение (для этого у QWidget есть метод grab()), полученное изображение переводится в текстуру OpenGL и последняя рендерится поверх видео средствами OpenGL. Добавив соответствующее окружение, был реализован универсальный механизм, который можно использовать для отображения поверх видео любых стандартных виджетов таким нестандартным способом.

    «Киоски» и виджеты


    Непростой была и задача по менеджменту дисплеев и распределению по ним фрагментов пользовательского интерфейса в режиме «киоска». ВКС терминал может работать в 2-х режимах – оконном, то есть как любое другое оконное приложение в среде рабочего стола операционной системы, и «режиме киоска» (то есть в операционной системе запущено только одно приложение с графическим интерфейсом – ВКСТ – и отсутствует среда рабочего стола).

    В оконном режиме все относительно просто: окнами управляет оконный менеджер среды рабочего стола, приложение при необходимости создает второе окно, а пользователь разносит окна по дисплеям так, как ему нужно. А вот в режиме «киоска» все куда сложнее, так как в системе нет менеджера окон и окно может быть только одно, и пользователь не имеет возможности его перемещать. Поэтому появилась задача автоматического детектирования события, например, подключения/отключения дисплея. При наступлении этого события необходимо было автоматически конфигурировать дисплеи и правильно разместить на них фрагменты пользовательского интерфейса.

    image

    Ответ пришел со стороны системной библиотеки ОС LINUX Xrandr, отвечающей за работу с дисплеями. Документации по ней в Интернете крайне мало, поэтому реализация шла с использованиеми примеров из сети Интернет, в том числе с Хабра. Кроме того, необходимо было придумать алгоритм распределения фрагментов интерфейса по дисплеям, а также встроить два различных окна в одно единственное. Последнее было реализовано следующим образом: то, что является окнами в оконном режиме, в режиме «киоска» является виджетами внутри одного большого окна, которое растягивается на 2 дисплея (если их 2). При этом необходимо сконфигурировать позиции дисплеев так, чтобы создавалось непрерывное виртуальное пространство (это осуществляется с помощью библиотеки XRandr), а потом задать геометрию внутренних виджетов внутри единственного глобального окна так, чтобы каждый попал на свой дисплей.

    Создаем российское


    Весь путь создания российской ВКС системы состоял и состоит из множества этапов, и GUI — это только верхушка айсберга. Самая заметная и не самая сложная. Однако комплексность решения, сочетание программных и программно-аппаратных компонентов, да и желание сделать технически и эстетически «красивую» систему создало немало трудностей на пути. Новые задачи породили нестандартные пути решения и помогли создать продукт, который не стыдно показать не только в России, но и за рубежом.

    Российские разработки уже давно доказали свою работоспособность, а в красивой оболочке и конкурентоспособность. Наши лайфхаки будут полезны каждому, кто серьезно занимается разработкой GUI, и надеемся, помогут другим разработчикам ускорить и упростить процесс создания современных оболочек для новых российских софтверных продуктов. Мы верим, что российские решения будут цениться в мире не меньше русского балета или черной икры.
    НТЦ ПРОТЕЙ
    Компания

    Комментарии 42

      +2

      Хотя бы расшифровали ВКС вначале. А то я ожидал описание разработки терминала для военно космических сил на С++ в пику Маску с его chromium и JavaScript для космонавтов.

        0

        Так точно, это же элементарные правила оформления "научных" публикаций.

          +1
          Отличное замечание, добавлено.
          +1

          А то действительно, терминал для начальника ВКС, чтобы он мог напечатать доклад министру обороны.

            0
            Такое в открытом доступе никто не напишет. А за коммент спасибо!
            0

            Здорово! Молодцы! Жаль, что пока для западных ВКС приходится софт на Qt делать, потому что в РФ удалёнки почти нет и зп ниже плинтуса. С гораздо большей радостью делал бы софт для отечественной оборонки!

              0
              С удаленкой все станет лучше, самоизоляция многому учит.
                –5

                Да, надеюсь — было бы здорово! После голосования за поправки и зп должны сильно вырасти! :)

              +2
              например, более современная технологя QML не позволяет рендерить видео с использованием произвольного OpenGL-контекста

              А можете более подробно описать эту проблему или ссылку дать?

                +1
                Насколько я помню (экспериментировали с GL-рендерингом в QML уже довольно давно), в общих чертах проблема следующая: как упоминается в статье, для оптимизации рендеринга аппаратно декодированного видео мы используем расширения из OpenGL ES 3.0, соответственно умолчательный GL-бэкэнд Qt-а (GLX) нам не подходит. При использовании EGL-бэкэнда (export QT_XCB_GL_INTEGRATION=xcb_egl) ломался штатный рендеринг QML — переставали компилироваться захардкоженные в QtQuick шейдеры, которые рассчитаны на обычный «старый» OpenGL. Менять самим все шейдеры из QtQuick и собирать свою либу посчитали слишком костыльным вариантом, хотя экспериментировали с ним и отчасти успешно. Были и другие проблемы после частичного решения проблем с шейдерами — например, краши на рендеринге в драйвере i965 при использовании кастомных (отдельно собранных) libEGL и i965 вместо нативных (поставляемых с ОС). Нативные же либы — слишком старые и не поддерживают нужные нам расширения.
                В общем, в итоге посчитали, что проще использовать Qt Widgets, чем неизвестно сколько (и с сомнительными шансами на успех) биться над решением кучи проблем в QML.
                  0
                  А не подскажете версию Qt c которой пробовали? Я недавно игрался с QML VideoOutput (как раз после того как надо было перенести OpenGL код с QOpenGLWidget на десктоп и стало понятно что еще раз изобретать велосипед желания нет) и вполне удалось завести код который одинаково работает на Nvidia/intel/PowerVR с оптимизацией dmabuf при необходимости.

                  Update:
                  Ну и возможно стоило попробовать пересобрать Qt с поддержкой gles?
                    0
                    Пробовали с Qt 5.8 — 5.11.

                    Ну и возможно стоило попробовать пересобрать Qt с поддержкой gles?

                    Это точно для нас не вариант. У нас сертифицируемое ПО, а сертифицировать ещё весь Qt желания мало :) Приходится довольствоваться тем Qt, который входит в состав дистрибутива линукса.
                  0
                  А можете раскрыть — по какой причине требуется рендеринг в произвольный OpenGL контекст? То есть в контекст, отличный от контекста окна?
                    0
                    Нет, рендерить нужно в контекст окна, но тот контекст окна, который создаёт Qt, нас не устраивает, потому что нам нужны EGL-расширения (об этом было немного в тексте статьи и в предыдущем моём комментарии), которые недоступны в умолчательном контексте. Подробнее раскрывать, наверное, не буду :)
                  0
                  Что находится в крейте под надписью Протей-ВКС? Какое назначение?
                    0
                    Это аппаратный сервер, собственно, ядро системы видеоконференцсвязи. Аппаратная платформа представляет собой телекоммуникационную кассету (шасси) 3U с набором специализированных плат. Собственная разработка компании.
                      0
                      Какие задачи решаются на этом сервере?
                        0
                        Обработка поступающих на него аудио и видео потоков, их микширование и кодирование, формирование независимых потоков для каждого участника конференции, управление абонентами, обеспечение работы системы в режиме АТС, запись переговоров.
                          0
                          Гипервизор, виртуализация?
                            0
                            Данный сервер оптимизирован для решения задач видеоконференцсвязи по производительности, надежности, функционалу. Но система видеоконференцсвязи нашей разработки может быть реализована как с использованием аппаратной части (сервера ПРОТЕЙ-ВКС), так и на вирутальной вычислительной платформе.
                              0
                              (шасси) 3U с набором специализированных плат

                              Что там может быть специализированного?
                                0

                                FPGA со своими реализациями микширования и т.п. вещей, например.
                                Но я сомневаюсь, что это действительно было нужно делать по причине того, то «бытовые» CPU не справились бы.

                                  0
                                  Специализированные по функциям. Каждая плата отвечает за свой набор функций: интеграционные, платы питания, коммутации (микширования) и управления. В зависимости от требования по производительности в шасси устанавливаются нужные комбинации плат. Это оказалось универсальнее, чем на 1 плату заводить все функции сразу.
                                    0
                                    С платой питания всё понятно… Но остальное что? Плата c CPU или плата GPCPU или плата с FPGA? Какую магистрально-модульную архитектуру используете?
                                      +1
                                      Про особенности разработки аппаратного обеспечения мы планируем написать отдельную статью. Следите за публикациями ;-)
                                      А так-то там есть и CPU/GPU (куда ж без них), и FPGA.
                                        0
                                        Хорошо. Буду ожидать.
                      0
                      -
                        +5
                        при таком использовании Qt за него нужно платить, насколько я понимаю, не так ли? Поделитесь опытом с этой стороны, пожалуйста.
                          0
                          Насколько я помню, входило в одну из покупных лицензий Линукса.
                            +1
                            при всём моём уважении — это невозможно.
                            у меня богатое воображение, но не могу представить себе, как в этом случае деньги дойдут до «The Qt Company»

                            покупка лицензий конечно должна была производиться не кодерами, а кодеры могут не «париться» вовсе — но не до такой же степени! :):)
                              0
                              Opensource компоненты никто не отменял :)
                                +1
                                Opensource компоненты не являются индульгенцией от оплаты лицензии.

                                Если ваше использование не подпадает под оплату — так и напишите. Издалека кажется, что продукт коммерчесикй — значит за Qt надо платить… Может у вас есть платные лицензии, просто в вашу компетенцию это не входит?
                                  +2
                                  Поясните, пожалуйста. Если они используют те компоненты Qt, которые под LGPL, то они должны 1) опубликовать те изменения, которые были сделаны непосредственно в данных компонентах, если таковые были и 2) предоставлять купившему ПО исходники Qt и возможность его перелинковать. Никакого требования покупать лицензию просто потому, что делается коммерческое ПО, нет.
                                    0
                                    потому и спрашивал, что мне было интересно, какой вариант был у данного варианта девелопмента.

                                    ответ «шло с платным дистрибутивом какого-то линукса» слишком размытый…

                                    «покупать ничего не пришлось из-за того, что уложились в GPL2» — годится, удовлетворяет моё любопытство…

                                    странно, что явно этот ответ не прозвучал, пришлось самим коллективно додумывать :):)
                          0
                          Этот продукт является конкурентом iva mcu?
                          iva mcu — тоже ВКС в рамках импортозамещения
                            0
                            Все Российские производители платформ ВКС в том или ином смысле конкурируют между собой на рынке, IVA и НТЦ ПРОТЕЙ не исключение.
                            Наша система ВКС среди прочих производителей имеет исключительную особенность: мы единственные(пока), кто производит собственную аппаратную платформы от терминальной до серверной части, а не отдельное ПО для установки на сторонний сервер.

                            Такой подход позволяет не только предотвратить разрыв текущих пользовательских сессий, но и обеспечивает гарантированное качество обслуживание для всех пользователей системы.
                              0
                              производит собственную аппаратную платформы

                              То есть модули 3U, входящие в шасси, собственной разработки?
                                0
                                Да, все платы собственной разработки, заточенные под наше ПО.
                                  0
                                  Процессорный модуль также разработан самостоятельно?
                                    0
                                    Нет, раньше разрабатывали, но оказалось нерентабельно. Теперь берем сторонних производителей.
                                      0
                                      Производители отечественные или зарубежные? Можете их назвать?
                                        0
                                        Мы сотрудничаем с отечественными и иностранными производителями. Применение тех или иных компонентов очень часто связано с поставленной технической задачей и с дальнейшими условиями применения оборудования, т.к. мы не просто «тиражируем» наши устройства, а адаптируем их по самым разным требованиям наших заказчиков.

                          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                          Самое читаемое