Описание

В сборке 5.2.1009 добавлена возможность описания собственных вариантов размещения картинок в микшере при помощи языка разметки на основе XML. Разработка собственного Java класса при этом не требуется.

Вариант размещения картинок представляет собой набор XML-файлов с расширением mix, расположенных в одном каталоге. Каждый файл должен описывать размещение картинок для определенного количества участников: 1, 2, 3, 4 и т.д. Имя файла должно начинаться с количества участников и следующего за ним символа подчеркивания, остальная часть имени файла должна содержать произвольный набор алфавитно-цифровых символов, например

1_test-mixer-layout-1-participant.mix
2_test-mixer-layout-2-participants.mix
3_test-mixer-layout-3-participants.mix
4_test-mixer-layout-4-participants.mix
...

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

Путь к этому каталогу может быть указан в настройке в файле flashphoner.properties

mixer_layout_dir=/opt/test-mixer-layout

В этом случае данный вариант размещения картинок будет применяться по умолчанию ко всем микшерам на сервере

Также путь к каталогу можно указать в REST API запросе /mixer/startup:

POST /rest-api/mixer/startup HTTP/1.1
HOST: 192.168.1.101:8081
Content-type: application/json
 
{
    "uri": "mixer://mixer1",
    "localStreamName": "mixer1_stream",
    "hasVideo": true,
    "hasAudio": false,
    "mixerLayoutDir": "/opt/mixer1-layout"
}

Применение варианта размещения картинок к активному микшеру

Начиная со сборки 5.2.1480, вариант размещения картинок можно применить к активному микшеру

POST /rest-api/mixer/set_parameter HTTP/1.1
HOST: 192.168.1.101:8081
Content-type: application/json
 
{
    "uri": "mixer://mixer1",
    "mixerLayoutDir": "/opt/mixer1-layout"
}

Вернуться к размещению картинок по умолчанию можно, указав пустое значение параметра mixerLayoutDir 

POST /rest-api/mixer/set_parameter HTTP/1.1
HOST: 192.168.1.101:8081
Content-type: application/json
 
{
    "uri": "mixer://mixer1",
    "mixerLayoutDir": ""
}

При этом будет применен класс размещения картинок, указанный в параметре микшера mixerLayoutClass.

Формат файла описания картинок

XML-файл описания картинок должен соответствовать следующей схеме

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="body" type="bodyType"/>
  <xs:complexType name="videoType">
    <xs:simpleContent>
      <xs:extension base="xs:string">
        <xs:attribute name="value" type="xs:string"/>
        <xs:attribute type="xs:int" name="x" default="0"/>
        <xs:attribute type="xs:int" name="y" default="0"/>
        <xs:attribute type="xs:string" name="width" default="0"/>
        <xs:attribute type="xs:string" name="height" default="0"/>
        <xs:attribute type="xs:string" name="align" default="LEFT"/>
        <xs:attribute type="xs:string" name="watermark"/>
        <xs:attribute type="xs:int" name="padding-left" default="0"/>
        <xs:attribute type="xs:int" name="padding-right" default="0"/>
        <xs:attribute type="xs:int" name="padding-top" default="0"/>
        <xs:attribute type="xs:int" name="padding-bottom" default="0"/>
        <xs:attribute type="xs:boolean" name="crop" default="false"/>
      </xs:extension>
    </xs:simpleContent>
  </xs:complexType>
  <xs:complexType name="divType">
    <xs:sequence minOccurs="0" maxOccurs="unbounded">
      <xs:choice>
        <xs:element name="video" type="videoType"/>
        <xs:element name="div" type="divType"/>
        <xs:element name="row" type="rowType"/>
      </xs:choice>
    </xs:sequence>
    <xs:attribute type="xs:int" name="x" default="0"/>
    <xs:attribute type="xs:int" name="y" default="0"/>
    <xs:attribute type="xs:string" name="width" default="0"/>
    <xs:attribute type="xs:string" name="height" default="0"/>
    <xs:attribute type="xs:string" name="align" default="LEFT"/>
    <xs:attribute type="xs:int" name="padding-left" default="0"/>
    <xs:attribute type="xs:int" name="padding-right" default="0"/>
    <xs:attribute type="xs:int" name="padding-top" default="0"/>
    <xs:attribute type="xs:int" name="padding-bottom" default="0"/>
  </xs:complexType>
  <xs:complexType name="rowType">
    <xs:sequence minOccurs="0" maxOccurs="unbounded">
      <xs:choice>
        <xs:element name="video" type="videoType"/>
        <xs:element name="div" type="divType"/>
        <xs:element name="row" type="rowType"/>
      </xs:choice>
    </xs:sequence>
    <xs:attribute type="xs:int" name="x" default="0"/>
    <xs:attribute type="xs:int" name="y" default="0"/>
    <xs:attribute type="xs:string" name="width" default="0"/>
    <xs:attribute type="xs:string" name="height" default="0"/>
    <xs:attribute type="xs:string" name="align" default="LEFT"/>
    <xs:attribute type="xs:int" name="padding-left" default="0"/>
    <xs:attribute type="xs:int" name="padding-right" default="0"/>
    <xs:attribute type="xs:int" name="padding-top" default="0"/>
    <xs:attribute type="xs:int" name="padding-bottom" default="0"/>
  </xs:complexType>
  <xs:complexType name="bodyType">
    <xs:sequence minOccurs="0" maxOccurs="unbounded">
      <xs:choice>
        <xs:element name="video" type="videoType"/>
        <xs:element name="div" type="divType"/>
        <xs:element name="row" type="rowType"/>
      </xs:choice>
    </xs:sequence>
    <xs:attribute type="xs:string" name="watermark"/>
  </xs:complexType>
</xs:schema>

Описания картинок проверяются по данной схеме, если валидация для какого-то количества участников не проходит, то будет использовано стандартное размещение картинок.

Поддерживаются следующие элементы:

Элемент body

Является контейнером верхнего уровня. Поддерживает следующие атрибуты:

Элемент div

Является контейнером для других div или video элементов. Поддерживает следующие атрибуты:

Атрибут align может принимать следующие значения

Если координаты левого верхнего угла заданы явно, то параметр align не применяется

Элемент row

В сборке 5.2.1052 добавлена поддержка элемента row, представляющего строку из одной или нескольких картинок. Поддерживает те же атрибуты, что и div

Элемент video

Представляет картинку потока в контейнере. Поддерживает те же атрибуты, что и div, с дополнительными атрибутами

Атрибут crop может принимать значения true или false.

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

Ширина картинки потока

Начиная со сборки 5.2.1052, ширина картинки может задаваться как в пикселях или процентах от ширины родительского элемента, так и в колонках. Для этого родительским элементом должен быть элемент row, например

<?xml version="1.0" encoding="utf-8"?>
<body>
  <row height="80%" align="CENTER">
    <video width="1col" align="INLINE_HORIZONTAL_CENTER"/>
    <video width="1col" align="INLINE_HORIZONTAL_CENTER"/>
  </row>
</body>

В этом случае, при разборе файла описания размер одной колонки вычисляется, как ширина строки, деленная на количество картинок одного уровня, затем для всех картинок в строке устанавливается размер в соответствии с указанным в атрибуте width количеством колонок.

Пример картинки микшера для вышеприведенного описания

Не допускается смешивать в элементах одного уровня ширину в колонках и в процентах!

Начиная со сборки 5.2.1094, ширина в колонках может быть указана также и для дочерних элементов div

<?xml version="1.0" encoding="utf-8"?>
<body>
  <div height="80%" align="CENTER">
    <video width="1col" align="INLINE_HORIZONTAL_CENTER"/>
    <video width="1col" align="INLINE_HORIZONTAL_CENTER"/>
  </row>
</body>

Высота картинки потока

Начиная со сборки 5.2.1094, высота картинки может задаваться как в пикселях или процентах от высоты родительского элемента, так и в строках

<?xml version="1.0" encoding="utf-8"?>
<body>
  <row height="1row" align="INLINE_VERTICAL_CENTER">
    <div width="1col" height="100%" align="INLINE_HORIZONTAL_CENTER"><video width="100%" height="100%" align="CENTER"/></div>
    <div width="1col" height="100%" align="INLINE_HORIZONTAL_CENTER"><video width="100%" height="100%" align="CENTER"/></div>
  </row>
  <row height="1row" align="INLINE_VERTICAL_CENTER">
    <div width="1col" height="100%" align="INLINE_HORIZONTAL_CENTER"/>
    <div width="1col" height="100%" align="INLINE_HORIZONTAL_CENTER"><video width="100%" height="100%" align="CENTER"/></div>
    <div width="1col" height="100%" align="INLINE_HORIZONTAL_CENTER"/>
  </row>
  <row height="1row" align="INLINE_VERTICAL_CENTER" />
</body>

В этом случае, при разборе файла описания размер одной строки вычисляется, как высота родительского элемента, деленная на количество элементов одного уровня, затем для всех элементов устанавливается размер в соответствии с указанным в атрибуте height количеством строк.

Пример картинки микшера для вышеприведенного описания

Не допускается смешивать в элементах одного уровня высоту в строках и в процентах!

Шаблон имени потока

Элемент video может задавать шаблон имени потока. Например

<video>test</video>

отобразит только поток с именем test, к другим потока этот элемент применяться не будет.

Шаблон может быть задан регулярным выражением, например

<video>test1.*</video>

В этом случае данный элемент отобразит поток с именем test1, test1#room1, test11 и т.д.

Если имя потока не соответствует ни одному шаблону, и в файле есть элемент video без шаблона, то для этого потока будет использован этот элемент. Например, рассмотрим описание картинки для одного участника

<?xml version="1.0" encoding="utf-8"?>
<body xsi:noNamespaceSchemaLocation="schema.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <div width="320" height="180" padding-left="0" padding-right="0" padding-bottom="0" align="TOP_CENTER">
    <video>test1.*</video>
  </div>
  <div width="320" height="180" padding-left="0" padding-right="0" padding-bottom="0" align="BOTTOM_CENTER">
    <video>test2.*</video>
  </div>
  <div width="160" height="90" padding-left="0" padding-right="0" padding-bottom="0" align="RIGHT">
    <video>.*</video>
  </div>
</body>

В этом случае поток test3 будет отображен в последнем video элементе.

Размещение потока в определенной позиции

В сборке 5.2.1950 добавлена возможность поместить картинку потока в заданную позицию по ее идентификатору. Это удобно в том случае, если невозможно воспользоваться шаблоном имени потока. Например, есть описание размещения картинок на три участника

<?xml version="1.0" encoding="utf-8"?>
<body>
  <row height="100%" align="CENTER">
    <video width="100%" id="desktop" align="CENTER"></video>
    <row height="20%" align="BOTTOM">
      <div width="1col" height="100%" align="INLINE_HORIZONTAL"><video width="95%" height="95%" id="speaker" align="RIGHT"/></div>
      <div width="1col" height="100%" align="INLINE_HORIZONTAL"><video width="95%" height="95%" id="participant" align="LEFT"/></div>
    </row>
  </row>
</body>

В этом случае можно переместить поток в позицию с идентификатором desktop при помощи REST API запроса /mixer/set_position :

POST /rest-api/mixer/set_position HTTP/1.1
Host: localhost:8081
Content-Type: application/json

{
    "uri": "mixer://mixer1",
    "remoteStreamName": "stream3",
    "videoPositionId": "desktop"
}

Также поток можно поместить в заданную позицию при его добавлении в микшер:

POST /rest-api/mixer/add HTTP/1.1
Host: localhost:8081
Content-Type: application/json

{
    "uri": "mixer://mixer1",
    "remoteStreamName": "stream1",
    "videoPositionId": "speaker"
}

Добавление водяного знака в поток микшера или в поток участника

В сборке 5.2.1051 добавлена возможность указать в описании количества участников картинку в формате PNG, которая будет применена в качестве водяного знака ко всему потоку микшера или к потоку отдельного участника.

Для применения водяного знака ко всему потоку микшера необходимо добавить атрибут watermark с указанием имени PNG файла к элементу body:

<body watermark="image.png">
  <row height="80%" align="CENTER">
    <video width="50%" align="INLINE_HORIZONTAL_CENTER"/>
    <video width="50%" align="INLINE_HORIZONTAL_CENTER"/>
  </row>
</body>

При этом PNG файл должен располагаться в каталоге описания размещения картинок:

[root@demo ~]# ls -l /opt/GridLayout
total 20
-rw-r--r-- 1 root root  106 Sep 30 13:10 1_participants.mix
-rw-r--r-- 1 root root  204 Oct 11 12:11 2_participants.mix
-rw-r--r-- 1 root root  409 Oct  8 13:18 3_participants.mix
-rw-r--r-- 1 root root  591 Oct  8 13:00 4_participants.mix
-rw-r--r-- 1 root root 3434 Oct  7 08:57 image.png

Пример выходного потока микшера с наложенным водяным знаком

Водяной знак может быть наложен и на поток отдельного участника, для этого необходимо добавить атрибут watermark с указанием имени PNG файла к элементу video:

<body>
  <row height="80%" align="CENTER">
    <video watermark="image.png" width="50%" align="INLINE_HORIZONTAL_CENTER"/>
    <video width="50%" align="INLINE_HORIZONTAL_CENTER"/>
  </row>
</body>

Описание демонстрации экрана для определенного количества участников

В сборке 5.2.1091 добавлена возможность описать вариант размещения картинок с демонстрацией экрана для определенного количества участников. Файлы описаний для демонстрации экрана должны иметь расширение .desktopmix и располагаться в том же каталоге варианта размещения картинок, что и файлы описаний без экрана:

1_participants.desktopmix
2_participants.desktopmix
3_participants.desktopmix
1_participants.mix
2_participants.mix
3_participants.mix

Поток экрана определяется по шаблону имени. Пример описания для двух участников и одного потока экрана

<?xml version="1.0" encoding="utf-8"?>
<body>
  <row height="80%" align="TOP">
    <video width="100%" align="CENTER">.*_desktop.*</video>
  </row>
  <row height="20%" align="BOTTOM">
    <div width="1col" height="100%" align="INLINE_HORIZONTAL_CENTER"><video width="90%" align="CENTER"/></div>
    <div width="1col" height="100%" align="INLINE_HORIZONTAL_CENTER"><video width="90%" align="CENTER"/></div>
  </row>
</body>

Обратите внимание, что с точки зрения микшера поток экрана - такой же участник, поэтому дескриптор на 3 потока в микшере в данном случае описывает два потока участников плюс экран одного из участников

Если файла *.desktopmix нет для заданного количества участников, и в микшер добавлен поток экрана, будет использовано стандартное размещение картинок для демонстрации экрана

Обработка ошибок

1. Если вариант размещения картинок не содержит описания для определенного количества участников, будет использован вариант, заданный в настройке

mixer_layout_class=com.flashphoner.media.mixer.video.presentation.GridLayout

По умолчанию, используется GridLayout

2. Если имя потока не соответствует ни одному шаблону в описании текущего количества участников, аудио и видео из такого потока не будет добавлено в выходной поток микшера

Примеры

Распределение картинок согласно именам потоков

Рассмотрим вариант размещения в микшере с выходным потоком 640x360 до трех участников. Обратите внимание, что в данном случае размеры картинок для всех потоков заданы явным образом, и не должны превышать размеров холста микшера.

Описание на одного участника:

<?xml version="1.0" encoding="utf-8"?>
<body xsi:noNamespaceSchemaLocation="schema.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <div width="320" height="180" padding-left="0" padding-right="0" padding-bottom="0" align="TOP_CENTER">
    <video crop="false">test1.*</video>
  </div>
  <div width="320" height="180" padding-left="0" padding-right="0" padding-bottom="0" align="BOTTOM_CENTER">
    <video crop="false">test2.*</video>
  </div>
</body>

Описание на двух участников

<?xml version="1.0" encoding="utf-8"?>
<body xsi:noNamespaceSchemaLocation="schema.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <div width="320" height="180" padding-left="0" padding-right="0" padding-bottom="0" align="TOP_CENTER">
    <video crop="false">test1.*</video>
  </div>
  <div width="320" height="180" padding-left="0" padding-right="0" padding-bottom="0" align="BOTTOM_CENTER">
    <video crop="false">test2.*</video>
  </div>
</body>

Описание на трех участников

<?xml version="1.0" encoding="utf-8"?>
<body xsi:noNamespaceSchemaLocation="schema.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <div width="320" height="180" padding-left="0" padding-right="0" padding-bottom="0" align="LEFT">
    <video crop="false">test1.*</video>
  </div>
  <div width="320" height="180" padding-left="0" padding-right="0" padding-bottom="0" align="RIGHT">
    <video crop="false">test2.*</video>
  </div>
  <div width="320" height="180" padding-left="0" padding-right="0" padding-bottom="0" align="BOTTOM_CENTER">
    <video crop="false">test3.*</video>
  </div>
</body>

Пример отображения потока test1

Пример отображения двух потоков test1 и test2

Пример отображения потоков test1, test2 и test3

Распределение картинок случайным образом, без строгого указания размера

Описание на одного участника:

<?xml version="1.0" encoding="utf-8"?>
<body>
  <row height="80%" align="CENTER">
    <video width="100%" align="CENTER"/>
  </row>
</body>

Описание на двух участников

<?xml version="1.0" encoding="utf-8"?>
<body>
  <row height="80%" align="CENTER">
    <video width="50%" align="INLINE_HORIZONTAL_CENTER"/>
    <video width="50%" align="INLINE_HORIZONTAL_CENTER"/>
  </row>
</body>

Описание на трех участников

<?xml version="1.0" encoding="utf-8"?>
<body>
  <row height="50%" align="INLINE_VERTICAL_CENTER">
    <div width="50%" height="100%" align="INLINE_HORIZONTAL_CENTER"><video width="100%" align="CENTER"/></div>
    <div width="50%" height="100%" align="INLINE_HORIZONTAL_CENTER"><video width="100%" align="CENTER"/></div>
  </row>
  <row height="50%" align="INLINE_VERTICAL_CENTER">
    <video width="100%" align="CENTER"/>
  </row>
</body>

Пример отображения одного потока

Пример отображения двух потоков

Пример отображения трех потоков

Инструмент для отображения размещения картинок

В сборке 5.2.1035 добавлен инструмент для отображения вариантов размещения картинок, запускаемый из командной строки

cd /usr/local/FlashphonerWebCallServer/tools
bash ./mixer_layout_tool.sh /path/to/mixer_layout -o=/path/to/output

Инструмент выводит в указанный каталог одну картинку в формате PNG на один файл описания количества участников.

Поддерживаются следующие параметры:

Инструмент использует текущие настройки микшера из файла flashphoner.properties

Примеры отображения

Рассмотрим примеры отображения варианта расположения картинок, приведенного выше

1. Картинка для одного участника с отображением рамки индикатора речи, имена потоков задаются явно

bash ./mixer_layout_tool.sh /opt/mixer_layout -o=/tmp -N=test1,test2,test3 -n=1 -a

Файл /tmp/1_test.png

2. Картинка для трех участников с указанием префикса и автоматическим формированием имен потоков

bash ./mixer_layout_tool.sh /opt/mixer_layouts -o=/tmp -p=test -n=3

Файл /tmp/3_test.png

Шаблоны имен потоков в описаниях данного варианта заданы явным образом как test1.*, test2.*, test3.*. При автоматической генерации имен потоков отсчет начинается с 0, поэтому для данного количества участников были созданы имена test0, test1, test2. В этом случае для недостающего потока test3 имя отображается как No stream for: test3.*

Обработка ошибок

1. Если размер картинки в описании варианта размещения картинок превышает размеры холста микшера, инструмент выведет сообщение об ошибке, файл PNG не будет сгенерирован:

13:54:49,232 INFO  toryLayoutController - Mixer got 2 frames. Using 2_test.mix descriptor
Computed layout would produce exception: java.lang.RuntimeException: Computed layout element: Layout{point=java.awt.Point[x=-106,y=0], dimension=java.awt.Dimension[width=852,height=478], frame:true} out of bounds
Please check configuration for this set of participants: [test1, test2]

Если создать микшер с таким вариантом размещения картинок, микшер будет закрыт с таким же сообщением об ошибке.

2. Если для какого-то количества участников нет файлов описания *.mix и *.desktopmix , инструмент выведет для этого количества участников картинку с суффиксом _fallback , используя стандартное размещение из настроек WCS.

Реализация стандартных размещений картинок на языке разметки

В архиве Layouts.tar.gz приведены примеры реализации стандартных размещений картинок в микшере, совместимый со сборками WCS, начиная с 5.2.1094 и новее

Архив содержит следующие каталоги:

Использование

1. Распаковать архив в каталог /opt

cd /opt
tar -xzf ~/Layouts.tar.gz 

2. В файле настроек flashphoner.properties указать нужное размещение картинок в качестве используемого по умолчанию

mixer_layout_dir=/opt/GridLayout

либо указывать нужное размещение при создании микшера по REST API

POST /rest-api/mixer/startup HTTP/1.1
Host: localhost:8081
Content-Type: application/json
 
{
    "uri": "mixer://mixer1",
    "localStreamName": "mixer1_stream",
    "hasVideo": true,
    "hasAudio": true,
    "mixerLayoutDir": "/opt/CenterNoPaddingGridLayout"
}

Реализация размещения "картинка-в-картинке"

Чтобы разместить одну из картинок, например, трансляцию рабочего стола, фоном для картинок остальных потоков, необходимо элемент row,  в котором отображается рабочий стол, сделать родительским для элементов row, содержащих картинки остальных потоков, например:

<?xml version="1.0" encoding="utf-8"?>
<body>
  <row height="100%" align="CENTER">
    <video width="100%" align="CENTER">.*_desktop.*</video>
    <row height="20%" align="BOTTOM">
      <div width="1col" height="100%" align="INLINE_HORIZONTAL"/>
      <div width="1col" height="100%" align="INLINE_HORIZONTAL"><video width="95%" height="95%" align="CENTER"/></div>
      <div width="1col" height="100%" align="INLINE_HORIZONTAL"><video width="95%" height="95%" align="CENTER"/></div>
      <div width="1col" height="100%" align="INLINE_HORIZONTAL"><video width="95%" height="95%" align="CENTER"/></div>
      <div width="1col" height="100%" align="INLINE_HORIZONTAL"/>
    </row>
  </row>
</body>

Пример описания размещения картинок до 10 участников + 1 поток рабочего стола приведен в архиве FullscreenDesktopLayout.tar.gz 

Поток рабочего стола должен иметь имя с суффиксом _desktop , например user1_desktop-room123456